From efc1cc257258da3e024d2db2609f1dbc83288de9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 18:00:34 +0800 Subject: [PATCH 01/26] refactor the validate deadline publish and rename to validate render passes --- .../publish/validate_deadline_publish.py | 43 ------ .../plugins/publish/validate_renderpasses.py | 137 ++++++++++++++++++ .../defaults/project_settings/max.json | 5 + .../schemas/schema_max_publish.json | 25 ++++ .../max/server/settings/publishers.py | 9 ++ server_addon/max/server/version.py | 2 +- 6 files changed, 177 insertions(+), 44 deletions(-) delete mode 100644 openpype/hosts/max/plugins/publish/validate_deadline_publish.py create mode 100644 openpype/hosts/max/plugins/publish/validate_renderpasses.py diff --git a/openpype/hosts/max/plugins/publish/validate_deadline_publish.py b/openpype/hosts/max/plugins/publish/validate_deadline_publish.py deleted file mode 100644 index b2f0e863f4..0000000000 --- a/openpype/hosts/max/plugins/publish/validate_deadline_publish.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import pyblish.api -from pymxs import runtime as rt -from openpype.pipeline.publish import ( - RepairAction, - ValidateContentsOrder, - PublishValidationError, - OptionalPyblishPluginMixin -) -from openpype.hosts.max.api.lib_rendersettings import RenderSettings - - -class ValidateDeadlinePublish(pyblish.api.InstancePlugin, - OptionalPyblishPluginMixin): - """Validates Render File Directory is - not the same in every submission - """ - - order = ValidateContentsOrder - families = ["maxrender"] - hosts = ["max"] - label = "Render Output for Deadline" - optional = True - actions = [RepairAction] - - def process(self, instance): - if not self.is_active(instance.data): - return - file = rt.maxFileName - filename, ext = os.path.splitext(file) - if filename not in rt.rendOutputFilename: - raise PublishValidationError( - "Render output folder " - "doesn't match the max scene name! " - "Use Repair action to " - "fix the folder file path.." - ) - - @classmethod - def repair(cls, instance): - container = instance.data.get("instance_node") - RenderSettings().render_output(container) - cls.log.debug("Reset the render output folder...") diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py new file mode 100644 index 0000000000..9e65f305a2 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -0,0 +1,137 @@ +import os +import pyblish.api +from pymxs import runtime as rt +from openpype.pipeline.publish import ( + RepairAction, + ValidateContentsOrder, + PublishValidationError, + OptionalPyblishPluginMixin +) +from openpype.hosts.max.api.lib import get_current_renderer +from openpype.hosts.max.api.lib_rendersettings import RenderSettings + + +class ValidateRenderPasses(OptionalPyblishPluginMixin, + pyblish.api.InstancePlugin): + """Validates Render Passes before Deadline Submission + """ + + order = ValidateContentsOrder + families = ["maxrender"] + hosts = ["max"] + label = "Validate Render Passes" + optional = True + actions = [RepairAction] + + def process(self, instance): + if not self.is_active(instance.data): + return + invalid = self.get_invalid(instance) + if invalid: + bullet_point_invalid_statement = "\n".join( + f"- {err_type}: {filepath}" for err_type, filepath + in invalid + ) + report = ( + "Invalid render passes found.\n\n" + f"{bullet_point_invalid_statement}\n\n" + "You can use repair action to fix the invalid filepath." + ) + raise PublishValidationError( + report, title="Invalid Render Passes") + + @classmethod + def get_invalid(cls, instance): + """Function to get invalid beauty render outputs and + render elements + + Args: + instance (pyblish.api.Instance): instance + filename (str): filename of the Max scene + + Returns: + list: list of invalid filename which doesn't match + with the project name + """ + invalid = [] + file = rt.maxFileName + filename, ext = os.path.splitext(file) + if filename not in rt.rendOutputFilename: + cls.log.error( + "Render output folder " + "doesn't match the max scene name! " + ) + invalid_folder_name = os.path.dirname( + rt.rendOutputFilename).replace( + "\\", "/").split("/")[-1] + invalid.append(("Invalid Render Output Folder", + invalid_folder_name)) + beauty_fname = os.path.basename(rt.rendOutputFilename) + ext = os.path.splitext(beauty_fname)[-1].lstrip(".") + invalid_image_format = cls.get_invalid_image_format( + cls, instance, ext) + invalid.extend(invalid_image_format) + renderer_class = get_current_renderer() + renderer = str(renderer_class).split(":")[0] + if renderer in [ + "ART_Renderer", + "Redshift_Renderer", + "V_Ray_6_Hotfix_3", + "V_Ray_GPU_6_Hotfix_3", + "Default_Scanline_Renderer", + "Quicksilver_Hardware_Renderer", + ]: + render_elem = rt.maxOps.GetCurRenderElementMgr() + render_elem_num = render_elem.NumRenderElements() + for i in range(render_elem_num): + renderlayer_name = render_elem.GetRenderElement(i) + renderpass = str(renderlayer_name).split(":")[-1] + rend_file = render_elem.GetRenderElementFilename(i) + if not rend_file: + cls.log.error(f"No filepath for {renderpass}") + invalid.append((f"Invalid {renderpass}", + "No filepath")) + rend_fname, ext = os.path.splitext( + os.path.basename(rend_file)) + if not rend_fname.lstrip(".").endswith(renderpass): + err_msg = ( + f"Filename for {renderpass} should be " + f"ended with {renderpass}" + ) + cls.log.error(err_msg) + invalid.append((f"Invalid {renderpass}", + os.path.basename(rend_file))) + invalid_image_format = cls.get_invalid_image_format( + cls, instance, ext) + invalid.extend(invalid_image_format) + elif renderer == "Arnold": + cls.log.debug( + "Temporarily not support to check on Arnold render.") + + return invalid + + def get_invalid_image_format(self, instance, ext): + """Function to check if the image format of the render outputs + aligns with that in the setting. + + Args: + instance (pyblish.api.Instance): instance + ext (str): image extension + + Returns: + list: list of files with invalid image format + """ + invalid = [] + settings = instance.context.data["project_settings"].get("max") + image_format = settings["RenderSettings"]["image_format"] + if ext.lstrip(".") != image_format: + msg = "Invalid image format for render outputs" + self.log.error(msg) + invalid.append((msg, ext.lstrip("."))) + return invalid + + @classmethod + def repair(cls, instance): + container = instance.data.get("instance_node") + RenderSettings().render_output(container) + cls.log.debug("Reset the render output folder...") diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index d1610610dc..293515f1a2 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -61,6 +61,11 @@ "optional": true, "family_plugins_mapping": [] }, + "ValidateRenderPasses": { + "enabled": true, + "optional": true, + "active": true + }, "ExtractModelObj": { "enabled": true, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index b4d85bda98..4dd3d519e8 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -88,6 +88,31 @@ } ] } + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateRenderPasses", + "label": "Validate Render Passes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + } + ] } ] }, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index d40d85a99b..a10e72b36a 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -68,6 +68,10 @@ class PublishersModel(BaseSettingsModel): default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" ) + ValidateRenderPasses: BasicValidateModel = Field( + default_factory=BasicValidateModel, + title="Validate Render Passes" + ) ExtractModelObj: BasicValidateModel = Field( default_factory=BasicValidateModel, title="Extract OBJ", @@ -106,6 +110,11 @@ DEFAULT_PUBLISH_SETTINGS = { "optional": True, "family_plugins_mapping": [] }, + "ValidateRenderPasses": { + "enabled": True, + "optional": True, + "active": True + }, "ExtractModelObj": { "enabled": True, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index bbab0242f6..1276d0254f 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.4" +__version__ = "0.1.5" From f0b8f254a113b51e96bfbc7f70ed1d440684412c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 18:33:37 +0800 Subject: [PATCH 02/26] add docstrings & fix the bug on the setting of validate render pass in legacy OP --- .../plugins/publish/validate_renderpasses.py | 21 ++++++++- .../schemas/schema_max_publish.json | 46 +++++++++---------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 9e65f305a2..f1bba81ecc 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -43,7 +43,26 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, @classmethod def get_invalid(cls, instance): """Function to get invalid beauty render outputs and - render elements + render elements. + + 1. Checking Render Output Folder matches the name of + the current Max Scene + e.g. The name of the current Max scene: + John_Doe.max + The expected render output directory: + {root[work]}/{project[name]}/{hierarchy} + /{asset}/work/{task[name]}/render/3dsmax/John_Doe/ + + 2. Checking the image extension(s) of the render output(s) + does not match with image format in OP/AYON setting. + e.g. The current image format in the setting: png + The expected render outputs: Joe_Doe.png + + 3. Checking the filename of render element is not ended + with the name of render element from the 3dsMax Render + Element Manager. + e.g. The name of render element: RsCryptomatte + The expected filename: {InstanceName}_RsCryptomatte.png Args: instance (pyblish.api.Instance): instance diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index 4dd3d519e8..d0da4029f5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -88,31 +88,31 @@ } ] } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateRenderPasses", + "label": "Validate Render Passes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" }, { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ValidateRenderPasses", - "label": "Validate Render Passes", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "active", - "label": "Active" - } - ] + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" } ] }, From 2590648f93e8f99e82ff0f6fcdd1af8abe134eb6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 19:27:01 +0800 Subject: [PATCH 03/26] big roy's comment - docstring tweaks and some log message tweaks --- .../plugins/publish/validate_renderpasses.py | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index f1bba81ecc..8472b56336 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -7,7 +7,6 @@ from openpype.pipeline.publish import ( PublishValidationError, OptionalPyblishPluginMixin ) -from openpype.hosts.max.api.lib import get_current_renderer from openpype.hosts.max.api.lib_rendersettings import RenderSettings @@ -45,23 +44,22 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, """Function to get invalid beauty render outputs and render elements. - 1. Checking Render Output Folder matches the name of - the current Max Scene - e.g. The name of the current Max scene: - John_Doe.max - The expected render output directory: - {root[work]}/{project[name]}/{hierarchy} - /{asset}/work/{task[name]}/render/3dsmax/John_Doe/ + 1. Check Render Output Folder matches the name of + the current Max Scene, e.g. + The name of the current Max scene: + John_Doe.max + The expected render output directory: + {root[work]}/{project[name]}/{hierarchy}/{asset}/ + work/{task[name]}/render/3dsmax/John_Doe/ - 2. Checking the image extension(s) of the render output(s) - does not match with image format in OP/AYON setting. - e.g. The current image format in the setting: png - The expected render outputs: Joe_Doe.png + 2. Check image extension(s) of the render output(s) + matches the image format in OP/AYON setting, e.g. + The current image format in settings: png + The expected render outputs: John_Doe.png - 3. Checking the filename of render element is not ended - with the name of render element from the 3dsMax Render - Element Manager. - e.g. The name of render element: RsCryptomatte + 3. Check filename of render element ends with the name of + render element from the 3dsMax Render Element Manager. + e.g. The name of render element: RsCryptomatte The expected filename: {InstanceName}_RsCryptomatte.png Args: @@ -90,8 +88,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid_image_format = cls.get_invalid_image_format( cls, instance, ext) invalid.extend(invalid_image_format) - renderer_class = get_current_renderer() - renderer = str(renderer_class).split(":")[0] + renderer = instance.data["renderer"] if renderer in [ "ART_Renderer", "Redshift_Renderer", @@ -107,17 +104,17 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, renderpass = str(renderlayer_name).split(":")[-1] rend_file = render_elem.GetRenderElementFilename(i) if not rend_file: - cls.log.error(f"No filepath for {renderpass}") + cls.log.error( + f"No filepath for render element {renderpass}") invalid.append((f"Invalid {renderpass}", "No filepath")) rend_fname, ext = os.path.splitext( os.path.basename(rend_file)) if not rend_fname.lstrip(".").endswith(renderpass): - err_msg = ( - f"Filename for {renderpass} should be " - f"ended with {renderpass}" + cls.log.error( + f"Filename for {renderpass} should " + f"end with {renderpass}" ) - cls.log.error(err_msg) invalid.append((f"Invalid {renderpass}", os.path.basename(rend_file))) invalid_image_format = cls.get_invalid_image_format( @@ -125,7 +122,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( - "Temporarily not support to check on Arnold render.") + "Renderpass validation not supported Arnold yet," + " validation skipped...") return invalid @@ -143,10 +141,13 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid = [] settings = instance.context.data["project_settings"].get("max") image_format = settings["RenderSettings"]["image_format"] - if ext.lstrip(".") != image_format: - msg = "Invalid image format for render outputs" + ext = ext.lstrip(".") + if ext != image_format: + msg = ( + f"Invalid image format {ext} for render outputs" + f"Should be : {image_format}") self.log.error(msg) - invalid.append((msg, ext.lstrip("."))) + invalid.append((msg, ext)) return invalid @classmethod From 38c6683756b4dbfd3dd9e276cbcdcc9070a91296 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 20:13:12 +0800 Subject: [PATCH 04/26] codes tweaks on log message if it errors out --- .../max/plugins/publish/validate_renderpasses.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 8472b56336..2ad8f5601a 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -76,7 +76,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, if filename not in rt.rendOutputFilename: cls.log.error( "Render output folder " - "doesn't match the max scene name! " + f"doesn't match the max scene name {filename} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( @@ -85,8 +85,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid_folder_name)) beauty_fname = os.path.basename(rt.rendOutputFilename) ext = os.path.splitext(beauty_fname)[-1].lstrip(".") - invalid_image_format = cls.get_invalid_image_format( - cls, instance, ext) + invalid_image_format = cls.get_invalid_image_format(instance, ext) invalid.extend(invalid_image_format) renderer = instance.data["renderer"] if renderer in [ @@ -117,8 +116,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, ) invalid.append((f"Invalid {renderpass}", os.path.basename(rend_file))) - invalid_image_format = cls.get_invalid_image_format( - cls, instance, ext) + invalid_image_format = cls.get_invalid_image_format(instance, ext) invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( @@ -127,7 +125,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, return invalid - def get_invalid_image_format(self, instance, ext): + @classmethod + def get_invalid_image_format(cls, instance, ext): """Function to check if the image format of the render outputs aligns with that in the setting. @@ -146,12 +145,13 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, msg = ( f"Invalid image format {ext} for render outputs" f"Should be : {image_format}") - self.log.error(msg) + cls.log.error(msg) invalid.append((msg, ext)) return invalid @classmethod def repair(cls, instance): container = instance.data.get("instance_node") + # TODO: need to rename the function of render_output RenderSettings().render_output(container) - cls.log.debug("Reset the render output folder...") + cls.log.debug("Finished repairing the render output folder and filenames.") From 204504b6406c14a3cec118756ba7d530f6c3366c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Jan 2024 20:15:05 +0800 Subject: [PATCH 05/26] hound shut --- openpype/hosts/max/plugins/publish/validate_renderpasses.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 2ad8f5601a..9b3064c393 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -116,7 +116,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, ) invalid.append((f"Invalid {renderpass}", os.path.basename(rend_file))) - invalid_image_format = cls.get_invalid_image_format(instance, ext) + invalid_image_format = cls.get_invalid_image_format( + instance, ext) invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( @@ -154,4 +155,5 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, container = instance.data.get("instance_node") # TODO: need to rename the function of render_output RenderSettings().render_output(container) - cls.log.debug("Finished repairing the render output folder and filenames.") + cls.log.debug("Finished repairing the render output " + "folder and filenames.") From bb6155738398f2e71c27f4012b72601d7ab3d911 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 17 Jan 2024 20:13:28 +0800 Subject: [PATCH 06/26] Debug message code tweaks --- openpype/hosts/max/plugins/publish/validate_renderpasses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 9b3064c393..7b85763c74 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -144,8 +144,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, ext = ext.lstrip(".") if ext != image_format: msg = ( - f"Invalid image format {ext} for render outputs" - f"Should be : {image_format}") + f"Invalid image format {ext} for render outputs.\n" + f"Should be: {image_format}") cls.log.error(msg) invalid.append((msg, ext)) return invalid From 9a8700977df33ccd239a1c6d4f091636d76ca649 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 26 Jan 2024 18:26:41 +0800 Subject: [PATCH 07/26] make sure also validate the instance name inside the filename and make sure renderpass validated right --- .../plugins/publish/validate_renderpasses.py | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index 7b85763c74..b3f5c0325b 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -84,8 +84,12 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid.append(("Invalid Render Output Folder", invalid_folder_name)) beauty_fname = os.path.basename(rt.rendOutputFilename) - ext = os.path.splitext(beauty_fname)[-1].lstrip(".") - invalid_image_format = cls.get_invalid_image_format(instance, ext) + beauty_name, ext = os.path.splitext(beauty_fname) + invalid_filenames = cls.get_invalid_filenames( + instance, beauty_name) + invalid.extend(invalid_filenames) + invalid_image_format = cls.get_invalid_image_format( + instance, ext.lstrip(".")) invalid.extend(invalid_image_format) renderer = instance.data["renderer"] if renderer in [ @@ -109,13 +113,9 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, "No filepath")) rend_fname, ext = os.path.splitext( os.path.basename(rend_file)) - if not rend_fname.lstrip(".").endswith(renderpass): - cls.log.error( - f"Filename for {renderpass} should " - f"end with {renderpass}" - ) - invalid.append((f"Invalid {renderpass}", - os.path.basename(rend_file))) + invalid_filenames = cls.get_invalid_filenames( + instance, rend_fname, renderpass=renderpass) + invalid.extend(invalid_filenames) invalid_image_format = cls.get_invalid_image_format( instance, ext) invalid.extend(invalid_image_format) @@ -126,6 +126,33 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, return invalid + @classmethod + def get_invalid_filenames(cls, instance, file_name, renderpass=None): + """Function to get invalid filenames from render outputs. + + Args: + instance (pyblish.api.Instance): instance + file_name (str): name of the file + renderpass (str, optional): name of the renderpass. Defaults to None. + + Returns: + list: invalid filenames + """ + invalid = [] + if instance.name not in file_name: + cls.log.error("The renderpass should have instance name inside.") + invalid.append((f"Invalid instance name", + file_name)) + if renderpass is not None: + if not file_name.rstrip(".").endswith(renderpass): + cls.log.error( + f"Filename for {renderpass} should " + f"end with {renderpass}" + ) + invalid.append((f"Invalid {renderpass}", + os.path.basename(file_name))) + return invalid + @classmethod def get_invalid_image_format(cls, instance, ext): """Function to check if the image format of the render outputs From 2b6f47035b1e9b21dc15c975c9ceb8584855ef57 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 26 Jan 2024 18:29:13 +0800 Subject: [PATCH 08/26] hound shut --- openpype/hosts/max/plugins/publish/validate_renderpasses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py index b3f5c0325b..e636b4e8fe 100644 --- a/openpype/hosts/max/plugins/publish/validate_renderpasses.py +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -133,7 +133,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, Args: instance (pyblish.api.Instance): instance file_name (str): name of the file - renderpass (str, optional): name of the renderpass. Defaults to None. + renderpass (str, optional): name of the renderpass. + Defaults to None. Returns: list: invalid filenames From d0b8ea08fbaa496421a04e78d331d34b13fe3f37 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Feb 2024 16:38:08 +0100 Subject: [PATCH 09/26] Add assertion for creating media pool item if not found Asserts creation of media pool item when not found for specified files. This ensures proper handling and error messaging in such cases. --- client/ayon_core/hosts/resolve/api/plugin.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index ccb20f712f..bcdc30d20f 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -405,6 +405,11 @@ class ClipLoader: files, self.active_bin ) + + assert media_pool_item, AssertionError( + "Cannot create media pool item for files: `{}`".format(files) + ) + _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) source_out = int(_clip_property("End")) From 3addd98805782356677c23303d62f992c28a4e1b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 27 Feb 2024 13:59:16 +0100 Subject: [PATCH 10/26] Add LoadError handling in ClipLoader class ClipLoader class in the plugin file now handles LoadError exceptions for creating media pool items when processing files. This change ensures proper error handling during media item creation. --- client/ayon_core/hosts/resolve/api/plugin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index bcdc30d20f..e95c9da82d 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -11,6 +11,7 @@ from ayon_core.pipeline import ( LoaderPlugin, Anatomy ) +from ayon_core.pipeline.load import LoadError from . import lib from .menu import load_stylesheet @@ -406,7 +407,7 @@ class ClipLoader: self.active_bin ) - assert media_pool_item, AssertionError( + assert media_pool_item, LoadError( "Cannot create media pool item for files: `{}`".format(files) ) @@ -480,6 +481,11 @@ class ClipLoader: files, self.active_bin ) + + assert media_pool_item, LoadError( + "Cannot create media pool item for files: `{}`".format(files) + ) + _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) From 254958f29bf7891c9fd41e23171f7dfbc6c4abe6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 27 Feb 2024 15:04:46 +0100 Subject: [PATCH 11/26] Add check for MediaPoolItem existence before processing. Improve data handling in timeline items retrieval function. - Added a check to skip processing if MediaPoolItem doesn't exist. - Enhanced data handling within the timeline items retrieval function. - this is for case some adjustment clips or fusion clips are on timeline --- client/ayon_core/hosts/resolve/api/lib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 5eb88afdcb..2d15c93f87 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -409,6 +409,9 @@ def get_current_timeline_items( } # get track item object and its color for clip_index, ti in enumerate(_clips[track_index]): + if not ti.GetMediaPoolItem(): + continue + data = _data.copy() data["clip"] = { "item": ti, From b23500187d5d70f92843fd659df90fdaebc331f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 29 Feb 2024 14:07:00 +0100 Subject: [PATCH 12/26] :recycle: remove context asserts and support for multiple paths --- client/ayon_core/cli.py | 13 +++--- client/ayon_core/cli_commands.py | 29 +++++++------ .../plugins/publish/collect_rendered_files.py | 43 ++++++++----------- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 88b574da76..0b59589ebc 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -102,19 +102,18 @@ def extractenvironments(output_json_path, project, asset, task, app, envgroup): @main_cli.command() -@click.argument("paths", nargs=-1) -@click.option("-t", "--targets", help="Targets module", default=None, +@click.argument("path", nargs=1) +@click.option("-t", "--targets", help="Targets", default=None, multiple=True) @click.option("-g", "--gui", is_flag=True, help="Show Publish UI", default=False) -def publish(paths, targets, gui): +def publish(path, targets, gui): """Start CLI publishing. - Publish collects json from paths provided as an argument. - More than one path is allowed. + Publish collects json from path provided as an argument. +S """ - - Commands.publish(list(paths), targets, gui) + Commands.publish(path, targets, gui) @main_cli.command(context_settings={"ignore_unknown_options": True}) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index a24710aef2..dc11187990 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -3,6 +3,7 @@ import os import sys import json +import warnings class Commands: @@ -41,21 +42,21 @@ class Commands: return click_func @staticmethod - def publish(paths, targets=None, gui=False): + def publish(path: str, targets: list=None, gui:bool=False) -> None: """Start headless publishing. - Publish use json from passed paths argument. + Publish use json from passed path argument. Args: - paths (list): Paths to jsons. - targets (string): What module should be targeted - (to choose validator for example) + path (str): Path to JSON. + targets (list of str): List of pyblish targets. gui (bool): Show publish UI. Raises: RuntimeError: When there is no path to process. - """ + RuntimeError: When executed with list of JSON paths. + """ from ayon_core.lib import Logger from ayon_core.lib.applications import ( get_app_environments_for_context, @@ -73,6 +74,11 @@ class Commands: import pyblish.api import pyblish.util + if not isinstance(path, str): + warnings.warn( + "Passing list of paths is deprecated.", + DeprecationWarning) + # Fix older jobs for src_key, dst_key in ( ("AVALON_PROJECT", "AYON_PROJECT_NAME"), @@ -95,11 +101,8 @@ class Commands: publish_paths = manager.collect_plugin_paths()["publish"] - for path in publish_paths: - pyblish.api.register_plugin_path(path) - - if not any(paths): - raise RuntimeError("No publish paths specified") + for plugin_path in publish_paths: + pyblish.api.register_plugin_path(plugin_path) app_full_name = os.getenv("AYON_APP_NAME") if app_full_name: @@ -111,7 +114,7 @@ class Commands: app_full_name, launch_type=LaunchTypes.farm_publish, ) - os.environ.update(env) + os.environ |= env pyblish.api.register_host("shell") @@ -122,7 +125,7 @@ class Commands: else: pyblish.api.register_target("farm") - os.environ["AYON_PUBLISH_DATA"] = os.pathsep.join(paths) + os.environ["AYON_PUBLISH_DATA"] = os.pathsep.join(path) os.environ["HEADLESS_PUBLISH"] = 'true' # to use in app lib log.info("Running publish ...") diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index ca88a7aa82..152771da6f 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -36,18 +36,18 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): def _load_json(self, path): path = path.strip('\"') - assert os.path.isfile(path), ( - "Path to json file doesn't exist. \"{}\"".format(path) - ) + + if not os.path.isfile(path): + raise FileNotFoundError( + f"Path to json file doesn't exist. \"{path}\"") + data = None with open(path, "r") as json_file: try: data = json.load(json_file) except Exception as exc: self.log.error( - "Error loading json: " - "{} - Exception: {}".format(path, exc) - ) + "Error loading json: %s - Exception: %s", path, exc) return data def _fill_staging_dir(self, data_object, anatomy): @@ -73,30 +73,23 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): data_err = "invalid json file - missing data" required = ["user", "comment", "job", "instances", "version"] - assert all(elem in data.keys() for elem in required), data_err + + if any(elem not in data.keys() for elem in required): + raise ValueError(data_err) + if "folderPath" not in data and "asset" not in data: - raise AssertionError(data_err) + raise ValueError(data_err) if "folderPath" not in data: data["folderPath"] = data.pop("asset") - # set context by first json file - ctx = self._context.data - - ctx["folderPath"] = ctx.get("folderPath") or data.get("folderPath") - ctx["intent"] = ctx.get("intent") or data.get("intent") - ctx["comment"] = ctx.get("comment") or data.get("comment") - ctx["user"] = ctx.get("user") or data.get("user") - ctx["version"] = ctx.get("version") or data.get("version") - - # basic sanity check to see if we are working in same context - # if some other json file has different context, bail out. - ctx_err = "inconsistent contexts in json files - %s" - assert ctx.get("folderPath") == data.get("folderPath"), ctx_err % "folderPath" - assert ctx.get("intent") == data.get("intent"), ctx_err % "intent" - assert ctx.get("comment") == data.get("comment"), ctx_err % "comment" - assert ctx.get("user") == data.get("user"), ctx_err % "user" - assert ctx.get("version") == data.get("version"), ctx_err % "version" + # ftrack credentials are passed as environment variables by Deadline + # to publish job, but Muster doesn't pass them. + if data.get("ftrack") and not os.environ.get("FTRACK_API_USER"): + ftrack = data.get("ftrack") + os.environ["FTRACK_API_USER"] = ftrack["FTRACK_API_USER"] + os.environ["FTRACK_API_KEY"] = ftrack["FTRACK_API_KEY"] + os.environ["FTRACK_SERVER"] = ftrack["FTRACK_SERVER"] # now we can just add instances from json file and we are done any_staging_dir_persistent = False From 7955ab5e88e36d199fbace8b0a490b7c3474a871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 1 Mar 2024 11:20:41 +0100 Subject: [PATCH 13/26] :bug: path as string --- client/ayon_core/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 0b59589ebc..52eadccdd4 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -102,7 +102,7 @@ def extractenvironments(output_json_path, project, asset, task, app, envgroup): @main_cli.command() -@click.argument("path", nargs=1) +@click.argument("path", required=True) @click.option("-t", "--targets", help="Targets", default=None, multiple=True) @click.option("-g", "--gui", is_flag=True, From 14385cf12aef8aff6f3d2d1ae1dd9a7868ac7591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 1 Mar 2024 11:26:34 +0100 Subject: [PATCH 14/26] :bug: raise exception instead of deprecation warning --- client/ayon_core/cli_commands.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index dc11187990..08933a5501 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -75,9 +75,7 @@ class Commands: import pyblish.util if not isinstance(path, str): - warnings.warn( - "Passing list of paths is deprecated.", - DeprecationWarning) + raise RuntimeError("Path to JSON must be a string.") # Fix older jobs for src_key, dst_key in ( From 25573cebd5d27e8c915bdb5102b093b3345e4e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 1 Mar 2024 11:49:04 +0100 Subject: [PATCH 15/26] :recycle: small refactors --- client/ayon_core/cli_commands.py | 4 ++-- client/ayon_core/plugins/publish/collect_rendered_files.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/cli_commands.py b/client/ayon_core/cli_commands.py index 08933a5501..f50ad61622 100644 --- a/client/ayon_core/cli_commands.py +++ b/client/ayon_core/cli_commands.py @@ -112,7 +112,7 @@ class Commands: app_full_name, launch_type=LaunchTypes.farm_publish, ) - os.environ |= env + os.environ.update(env) pyblish.api.register_host("shell") @@ -123,7 +123,7 @@ class Commands: else: pyblish.api.register_target("farm") - os.environ["AYON_PUBLISH_DATA"] = os.pathsep.join(path) + os.environ["AYON_PUBLISH_DATA"] = path os.environ["HEADLESS_PUBLISH"] = 'true' # to use in app lib log.info("Running publish ...") diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index 152771da6f..8a60e7619d 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -74,7 +74,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): required = ["user", "comment", "job", "instances", "version"] - if any(elem not in data.keys() for elem in required): + if any(elem not in data for elem in required): raise ValueError(data_err) if "folderPath" not in data and "asset" not in data: From 1204c5f2c736cf0b16403569bfcf8d1850369cdc Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 4 Mar 2024 21:13:45 +0800 Subject: [PATCH 16/26] tweaks on the debug message --- .../hosts/max/plugins/publish/validate_renderpasses.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 52c4c14367..1e4990ae3a 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -75,8 +75,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, filename, ext = os.path.splitext(file) if filename not in rt.rendOutputFilename: cls.log.error( - "Render output folder " - f"doesn't match the max scene name {filename} " + "Render output folder must include" + f"the max scene name {filename} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( @@ -121,9 +121,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, invalid.extend(invalid_image_format) elif renderer == "Arnold": cls.log.debug( - "Renderpass validation not supported Arnold yet," + "Renderpass validation does not support Arnold yet," " validation skipped...") - return invalid @classmethod From e01de841c0490dfd96702256c1cdcfeb2c1dda65 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 4 Mar 2024 21:25:00 +0800 Subject: [PATCH 17/26] missing space --- .../hosts/max/plugins/publish/validate_renderpasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 1e4990ae3a..4e2298045c 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -76,7 +76,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, if filename not in rt.rendOutputFilename: cls.log.error( "Render output folder must include" - f"the max scene name {filename} " + f" the max scene name {filename} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( From 4565db8841e6ad3295d037055edd192632c7aec9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 12 Mar 2024 10:46:15 +0100 Subject: [PATCH 18/26] ignoring clips without linked mediapoolitems --- client/ayon_core/hosts/resolve/api/lib.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 2d15c93f87..731da6fc6f 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -330,19 +330,25 @@ def get_timeline_item(media_pool_item: object, Returns: object: resolve.TimelineItem """ - _clip_property = media_pool_item.GetClipProperty - clip_name = _clip_property("File Name") + clip_name = media_pool_item.GetClipProperty("File Name") output_timeline_item = None timeline = timeline or get_current_timeline() with maintain_current_timeline(timeline): # search the timeline for the added clip - for _ti_data in get_current_timeline_items(): - _ti_clip = _ti_data["clip"]["item"] - _ti_clip_property = _ti_clip.GetMediaPoolItem().GetClipProperty - if clip_name in _ti_clip_property("File Name"): - output_timeline_item = _ti_clip + for ti_data in get_current_timeline_items(): + ti_clip_item = ti_data["clip"]["item"] + ti_media_pool_item = ti_clip_item.GetMediaPoolItem() + + # Skip items that do not have a media pool item, like for example + # an "Adjustment Clip" or a "Fusion Composition" from the effects + # toolbox + if not ti_media_pool_item: + continue + + if clip_name in ti_media_pool_item.GetClipProperty("File Name"): + output_timeline_item = ti_clip_item return output_timeline_item From 466a3c977cf6be9150a738f50476b72d10eb5903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 12 Mar 2024 10:57:38 +0100 Subject: [PATCH 19/26] reversing changes since this situation will not happen --- client/ayon_core/hosts/resolve/api/plugin.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/client/ayon_core/hosts/resolve/api/plugin.py index e95c9da82d..ccb20f712f 100644 --- a/client/ayon_core/hosts/resolve/api/plugin.py +++ b/client/ayon_core/hosts/resolve/api/plugin.py @@ -11,7 +11,6 @@ from ayon_core.pipeline import ( LoaderPlugin, Anatomy ) -from ayon_core.pipeline.load import LoadError from . import lib from .menu import load_stylesheet @@ -406,11 +405,6 @@ class ClipLoader: files, self.active_bin ) - - assert media_pool_item, LoadError( - "Cannot create media pool item for files: `{}`".format(files) - ) - _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) source_out = int(_clip_property("End")) @@ -481,11 +475,6 @@ class ClipLoader: files, self.active_bin ) - - assert media_pool_item, LoadError( - "Cannot create media pool item for files: `{}`".format(files) - ) - _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) From 4cf6e1a443412314cdd1e7f69a050082a56540af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 12 Mar 2024 10:58:45 +0100 Subject: [PATCH 20/26] Update client/ayon_core/hosts/resolve/api/lib.py --- client/ayon_core/hosts/resolve/api/lib.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/client/ayon_core/hosts/resolve/api/lib.py index 731da6fc6f..dd97303383 100644 --- a/client/ayon_core/hosts/resolve/api/lib.py +++ b/client/ayon_core/hosts/resolve/api/lib.py @@ -415,9 +415,6 @@ def get_current_timeline_items( } # get track item object and its color for clip_index, ti in enumerate(_clips[track_index]): - if not ti.GetMediaPoolItem(): - continue - data = _data.copy() data["clip"] = { "item": ti, From 587bfe462f6548b7bd7463ad8d9d4285ae98f886 Mon Sep 17 00:00:00 2001 From: moonyuet Date: Mon, 18 Mar 2024 12:15:58 +0100 Subject: [PATCH 21/26] code tweaks according to big roy's comment --- .../plugins/publish/validate_renderpasses.py | 27 +++++++++---------- .../max/server/settings/publishers.py | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 4e2298045c..0fbdb2940e 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -12,19 +12,16 @@ from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings class ValidateRenderPasses(OptionalPyblishPluginMixin, pyblish.api.InstancePlugin): - """Validates Render Passes before Deadline Submission + """Validates Render Passes before farm submission """ order = ValidateContentsOrder families = ["maxrender"] hosts = ["max"] label = "Validate Render Passes" - optional = True actions = [RepairAction] def process(self, instance): - if not self.is_active(instance.data): - return invalid = self.get_invalid(instance) if invalid: bullet_point_invalid_statement = "\n".join( @@ -64,7 +61,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, Args: instance (pyblish.api.Instance): instance - filename (str): filename of the Max scene + workfile_name (str): filename of the Max scene Returns: list: list of invalid filename which doesn't match @@ -72,11 +69,11 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, """ invalid = [] file = rt.maxFileName - filename, ext = os.path.splitext(file) - if filename not in rt.rendOutputFilename: + workfile_name, ext = os.path.splitext(file) + if workfile_name not in rt.rendOutputFilename: cls.log.error( "Render output folder must include" - f" the max scene name {filename} " + f" the max scene name {workfile_name} " ) invalid_folder_name = os.path.dirname( rt.rendOutputFilename).replace( @@ -104,13 +101,11 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, render_elem_num = render_elem.NumRenderElements() for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) - renderpass = str(renderlayer_name).split(":")[-1] + renderpass = str(renderlayer_name).rsplit(":", 1)[-1] rend_file = render_elem.GetRenderElementFilename(i) if not rend_file: - cls.log.error( - f"No filepath for render element {renderpass}") - invalid.append((f"Invalid {renderpass}", - "No filepath")) + continue + rend_fname, ext = os.path.splitext( os.path.basename(rend_file)) invalid_filenames = cls.get_invalid_filenames( @@ -123,6 +118,10 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, cls.log.debug( "Renderpass validation does not support Arnold yet," " validation skipped...") + else: + cls.log.debug( + "Skipping render element validation " + f"for renderer : {renderer}") return invalid @classmethod @@ -140,7 +139,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, """ invalid = [] if instance.name not in file_name: - cls.log.error("The renderpass should have instance name inside.") + cls.log.error("The renderpass filename should contain the instance name.") invalid.append((f"Invalid instance name", file_name)) if renderpass is not None: diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 819bca1509..5e1b348d92 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -191,7 +191,7 @@ DEFAULT_PUBLISH_SETTINGS = { }, "ValidateRenderPasses": { "enabled": True, - "optional": True, + "optional": False, "active": True }, "ExtractModelObj": { From 53981f3fe1f15ef09f0f19428df1d106380f8e63 Mon Sep 17 00:00:00 2001 From: moonyuet Date: Mon, 18 Mar 2024 14:05:34 +0100 Subject: [PATCH 22/26] report detail filename in debug message --- .../hosts/max/plugins/publish/validate_renderpasses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py index 0fbdb2940e..ba948747b9 100644 --- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py +++ b/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py @@ -121,7 +121,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, else: cls.log.debug( "Skipping render element validation " - f"for renderer : {renderer}") + f"for renderer: {renderer}") return invalid @classmethod @@ -146,7 +146,7 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin, if not file_name.rstrip(".").endswith(renderpass): cls.log.error( f"Filename for {renderpass} should " - f"end with {renderpass}" + f"end with {renderpass}: {file_name}" ) invalid.append((f"Invalid {renderpass}", os.path.basename(file_name))) From 0ba8d7e6456b1b8f29333acc16f8b1d71333cb2e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 16:47:40 +0100 Subject: [PATCH 23/26] fix super call in class method --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index eaf93725f4..c25e99e92e 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -657,7 +657,7 @@ class Loader(LoaderPlugin): @classmethod def apply_settings(cls, project_settings): - super(Loader, cls).apply_settings(project_settings) + LoaderPlugin.apply_settings(cls, project_settings) cls.load_settings = project_settings['maya']['load'] def get_custom_namespace_and_group(self, context, options, loader_key): From c62993a394308fcf1ac45f6e25e7c66a62bdd023 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 16:59:07 +0100 Subject: [PATCH 24/26] remove cls passed to class method --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index c25e99e92e..2de029c516 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -657,7 +657,7 @@ class Loader(LoaderPlugin): @classmethod def apply_settings(cls, project_settings): - LoaderPlugin.apply_settings(cls, project_settings) + LoaderPlugin.apply_settings(project_settings) cls.load_settings = project_settings['maya']['load'] def get_custom_namespace_and_group(self, context, options, loader_key): From e913eefa0796cd71a404dc3ca283a9837aef1b0c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 16:59:15 +0100 Subject: [PATCH 25/26] fix get_product_name call --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index 2de029c516..f086295b57 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -608,7 +608,7 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase): return get_product_name( project_name, task_name, - task_type + task_type, host_name, self.layer_instance_prefix or self.product_type, variant, From 24a00cd96a723ad5ad88af85238596b7bd3c5cb2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Mar 2024 17:03:37 +0100 Subject: [PATCH 26/26] keep super class call --- client/ayon_core/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/client/ayon_core/hosts/maya/api/plugin.py index f086295b57..bdb0cb1c99 100644 --- a/client/ayon_core/hosts/maya/api/plugin.py +++ b/client/ayon_core/hosts/maya/api/plugin.py @@ -657,7 +657,7 @@ class Loader(LoaderPlugin): @classmethod def apply_settings(cls, project_settings): - LoaderPlugin.apply_settings(project_settings) + super(Loader, cls).apply_settings(project_settings) cls.load_settings = project_settings['maya']['load'] def get_custom_namespace_and_group(self, context, options, loader_key):