From c6d72a6488b1c1588cc753ac831906a41892f067 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 28 Mar 2024 19:01:23 +0000 Subject: [PATCH 01/30] Add render farm button on write nodes. --- client/ayon_core/hosts/nuke/api/lib.py | 26 ++++ client/ayon_core/hosts/nuke/api/utils.py | 166 ++++++++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 4fcba8d2d4..22428fd657 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1022,6 +1022,16 @@ def script_name(): return nuke.root().knob("name").value() +def add_button_render_farm(node): + name = "renderFarm" + label = "Render Farm" + value = "from ayon_core.hosts.nuke.api.utils import submit_headless_farm;" + value += "submit_headless_farm(nuke.thisNode())" + knob = nuke.PyScript_Knob(name, label, value) + knob.clearFlag(nuke.STARTLINE) + node.addKnob(knob) + + def add_button_write_to_read(node): name = "createReadNode" label = "Read From Rendered" @@ -1144,6 +1154,19 @@ def create_write_node( Return: node (obj): group node with avalon data as Knobs ''' + # Ensure name does not contain any invalid characters. + special_characters = set("!@#$%^&*()=[]{}|\\;',.<>/?~+-") + found_special_characters = [] + + # Check each character in the node name + for char in name: + if char in special_characters: + found_special_characters.append(char) + + msg = f"Special characters found in name \"{name}\": " + msg += f"{' '.join(found_special_characters)}" + assert not found_special_characters, msg + prenodes = prenodes or [] # filtering variables @@ -1268,6 +1291,9 @@ def create_write_node( link.setFlag(0x1000) GN.addKnob(link) + # Adding render farm submission button. + add_button_render_farm(GN) + # adding write to read button add_button_write_to_read(GN) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 1bfc1919fa..5a643d05d8 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -1,11 +1,18 @@ import os import re +import traceback +from datetime import datetime +import shutil import nuke -from ayon_core import resources +from pyblish import util from qtpy import QtWidgets +from ayon_core import resources +from ayon_core.pipeline import registered_host +from ayon_core.tools.utils import show_message_dialog + def set_context_favorites(favorites=None): """ Adding favorite folders to nuke's browser @@ -142,3 +149,160 @@ def is_headless(): bool: headless """ return QtWidgets.QApplication.instance() is None + + +def create_error_report(context): + error_message = "" + success = True + for result in context.data["results"]: + if result["success"]: + continue + + success = False + + err = result["error"] + formatted_traceback = "".join( + traceback.format_exception( + type(err), + err, + err.__traceback__ + ) + ) + fname = result["plugin"].__module__ + if 'File "", line' in formatted_traceback: + _, lineno, func, msg = err.traceback + fname = os.path.abspath(fname) + formatted_traceback = formatted_traceback.replace( + 'File "", line', + 'File "{0}", line'.format(fname) + ) + + err = result["error"] + error_message += "\n" + error_message += formatted_traceback + + return success, error_message + + +def submit_headless_farm(node): + # Ensure code is executed in root context. + if nuke.root() == nuke.thisNode(): + _submit_headless_farm(node) + else: + # If not in root context, move to the root context and then execute the + # code. + with nuke.root(): + _submit_headless_farm(node) + + +def _submit_headless_farm(node): + context = util.collect() + + success, error_report = create_error_report(context) + + if not success: + show_message_dialog( + "Collection Errors", error_report, level="critical" + ) + return + + # Find instance for node and workfile. + instance = None + instance_workfile = None + indexes_to_remove = [] + for count, Instance in enumerate(context): + if Instance.data["family"] == "workfile": + instance_workfile = Instance + continue + + instance_node = Instance.data["transientData"]["node"] + if node.name() == instance_node.name(): + instance = Instance + else: + indexes_to_remove.append(count) + + if instance is None: + show_message_dialog( + "Collection Error", + "Could not find the instance from the node.", + level="critical" + ) + return + + # Enable for farm publishing. + instance.data["farm"] = True + instance.data["transfer"] = False + + # Clear the families as we only want the main family, ei. no review etc. + instance.data["families"] = [] + + # Use the workfile instead of published. + publish_attributes = instance.data["publish_attributes"] + publish_attributes["NukeSubmitDeadline"]["use_published_workfile"] = False + + # Disable version validation. + instance.data.pop("latestVersion") + instance_workfile.data.pop("latestVersion") + + # Remove all other instances. + indexes_to_remove.sort(reverse=True) + for i in indexes_to_remove: + if 0 <= i < len(context): + del context[i] + + # Validate + util.validate(context) + + success, error_report = create_error_report(context) + + if not success: + show_message_dialog( + "Validation Errors", error_report, level="critical" + ) + return + + # Extraction. + util.extract(context) + + success, error_report = create_error_report(context) + + if not success: + show_message_dialog( + "Extraction Errors", error_report, level="critical" + ) + return + + # Save the workfile. + host = registered_host() + host.save_file(host.current_file()) + + # Copy the workfile to a timestamped copy. + current_datetime = datetime.now() + formatted_timestamp = current_datetime.strftime("%Y%m%d%H%M%S") + base, ext = os.path.splitext(host.current_file()) + + directory = os.path.join(os.path.dirname(base), "farm_submissions") + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "{}_{}{}".format( + os.path.basename(base), formatted_timestamp, ext + ) + path = os.path.join(directory, filename).replace("\\", "/") + context.data["currentFile"] = path + shutil.copy(host.current_file(), path) + + # Continue to submission. + util.integrate(context) + + success, error_report = create_error_report(context) + + if not success: + show_message_dialog( + "Extraction Errors", error_report, level="critical" + ) + return + + show_message_dialog( + "Submission Successful", "Submission to the farm was successful." + ) From eca60b00068b891d0f5c9a80d632f7854a454e43 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 5 Apr 2024 17:34:28 +0100 Subject: [PATCH 02/30] Review feedback --- client/ayon_core/hosts/nuke/api/lib.py | 9 +++++---- client/ayon_core/hosts/nuke/api/utils.py | 16 +++------------- .../nuke/plugins/create/create_write_image.py | 5 ++++- .../plugins/create/create_write_prerender.py | 6 +++++- .../nuke/plugins/create/create_write_render.py | 6 +++++- .../nuke/server/settings/create_plugins.py | 6 +++++- server_addon/nuke/server/version.py | 2 +- 7 files changed, 28 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 22428fd657..63e6ddef0f 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1022,9 +1022,9 @@ def script_name(): return nuke.root().knob("name").value() -def add_button_render_farm(node): - name = "renderFarm" - label = "Render Farm" +def add_button_headless_farm_submission(node): + name = "headlessFarmSubmission" + label = "Headless Farm Submission" value = "from ayon_core.hosts.nuke.api.utils import submit_headless_farm;" value += "submit_headless_farm(nuke.thisNode())" knob = nuke.PyScript_Knob(name, label, value) @@ -1292,7 +1292,8 @@ def create_write_node( GN.addKnob(link) # Adding render farm submission button. - add_button_render_farm(GN) + if data.get("headless_farm_submission", False): + add_button_headless_farm_submission(GN) # adding write to read button add_button_write_to_read(GN) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 5a643d05d8..9528ad3d4c 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -209,8 +209,7 @@ def _submit_headless_farm(node): # Find instance for node and workfile. instance = None instance_workfile = None - indexes_to_remove = [] - for count, Instance in enumerate(context): + for Instance in context: if Instance.data["family"] == "workfile": instance_workfile = Instance continue @@ -219,7 +218,7 @@ def _submit_headless_farm(node): if node.name() == instance_node.name(): instance = Instance else: - indexes_to_remove.append(count) + Instance.data["active"] = False if instance is None: show_message_dialog( @@ -244,12 +243,6 @@ def _submit_headless_farm(node): instance.data.pop("latestVersion") instance_workfile.data.pop("latestVersion") - # Remove all other instances. - indexes_to_remove.sort(reverse=True) - for i in indexes_to_remove: - if 0 <= i < len(context): - del context[i] - # Validate util.validate(context) @@ -272,11 +265,8 @@ def _submit_headless_farm(node): ) return - # Save the workfile. - host = registered_host() - host.save_file(host.current_file()) - # Copy the workfile to a timestamped copy. + host = registered_host() current_datetime = datetime.now() formatted_timestamp = current_datetime.strftime("%Y%m%d%H%M%S") base, ext = os.path.splitext(host.current_file()) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py index 770726e34f..046b99f6b0 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py @@ -65,12 +65,15 @@ def _get_frame_source_number(self): ) def create_instance_node(self, product_name, instance_data): + settings = self.project_settings["nuke"]["create"]["CreateWriteImage"] + settings = settings["instance_attributes"] # add fpath_template write_data = { "creator": self.__class__.__name__, "productName": product_name, - "fpath_template": self.temp_rendering_path_template + "fpath_template": self.temp_rendering_path_template, + "headless_farm_submission": "headless_farm_submission" in settings } write_data.update(instance_data) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py index 96ac2fac9c..df906c9c25 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py @@ -46,11 +46,15 @@ def get_pre_create_attr_defs(self): return attr_defs def create_instance_node(self, product_name, instance_data): + settings = self.project_settings["nuke"]["create"] + settings = settings["CreateWritePrerender"]["instance_attributes"] + # add fpath_template write_data = { "creator": self.__class__.__name__, "productName": product_name, - "fpath_template": self.temp_rendering_path_template + "fpath_template": self.temp_rendering_path_template, + "headless_farm_submission": "headless_farm_submission" in settings } write_data.update(instance_data) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py index 24bddb3d26..16bce64ec6 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py @@ -40,11 +40,15 @@ def get_pre_create_attr_defs(self): return attr_defs def create_instance_node(self, product_name, instance_data): + settings = self.project_settings["nuke"]["create"]["CreateWriteRender"] + settings = settings["instance_attributes"] + # add fpath_template write_data = { "creator": self.__class__.__name__, "productName": product_name, - "fpath_template": self.temp_rendering_path_template + "fpath_template": self.temp_rendering_path_template, + "headless_farm_submission": "headless_farm_submission" in settings } write_data.update(instance_data) diff --git a/server_addon/nuke/server/settings/create_plugins.py b/server_addon/nuke/server/settings/create_plugins.py index 6bdc5ee5ad..897a467118 100644 --- a/server_addon/nuke/server/settings/create_plugins.py +++ b/server_addon/nuke/server/settings/create_plugins.py @@ -12,7 +12,11 @@ def instance_attributes_enum(): return [ {"value": "reviewable", "label": "Reviewable"}, {"value": "farm_rendering", "label": "Farm rendering"}, - {"value": "use_range_limit", "label": "Use range limit"} + {"value": "use_range_limit", "label": "Use range limit"}, + { + "value": "headless_farm_submission", + "label": "Headless Farm Submission" + } ] diff --git a/server_addon/nuke/server/version.py b/server_addon/nuke/server/version.py index 569b1212f7..0c5c30071a 100644 --- a/server_addon/nuke/server/version.py +++ b/server_addon/nuke/server/version.py @@ -1 +1 @@ -__version__ = "0.1.10" +__version__ = "0.1.11" From 85140058ee6d4397485b33974abb78f7509a88f0 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 12 Apr 2024 15:34:47 +0100 Subject: [PATCH 03/30] Code cosmetics --- client/ayon_core/hosts/nuke/api/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 9528ad3d4c..a11f6e023b 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -209,16 +209,16 @@ def _submit_headless_farm(node): # Find instance for node and workfile. instance = None instance_workfile = None - for Instance in context: - if Instance.data["family"] == "workfile": - instance_workfile = Instance + for _instance in context: + if _instance.data["family"] == "workfile": + instance_workfile = _instance continue - instance_node = Instance.data["transientData"]["node"] + instance_node = _instance.data["transientData"]["node"] if node.name() == instance_node.name(): - instance = Instance + instance = _instance else: - Instance.data["active"] = False + _instance.data["active"] = False if instance is None: show_message_dialog( From cd45e9a41c3be122d5b8b95ab11f1234dd77bda8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 12 Apr 2024 15:43:31 +0100 Subject: [PATCH 04/30] docstring --- client/ayon_core/hosts/nuke/api/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index a11f6e023b..56ba581e1c 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -196,6 +196,16 @@ def submit_headless_farm(node): def _submit_headless_farm(node): + """Headless farm submission + + This function prepares the context for farm submission, validates it, + extracts relevant data, copies the current workfile to a timestamped copy, + and submits the job to the farm. + + Args: + node (Node): The node for which the farm submission is being made. + """ + context = util.collect() success, error_report = create_error_report(context) From 4f70d30ea59c2ef6cde9383edea860a7bfc154bb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 12 Apr 2024 15:47:12 +0100 Subject: [PATCH 05/30] docstring --- client/ayon_core/hosts/nuke/api/utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 56ba581e1c..94582f75f1 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -152,6 +152,19 @@ def is_headless(): def create_error_report(context): + """Create an error report based on the given pyblish context. + + This function iterates through the results in the context and formats any + errors into a comprehensive error report. + + Args: + context (dict): Pyblish context. + + Returns: + tuple: A tuple containing a boolean indicating success and a string + representing the error message. + """ + error_message = "" success = True for result in context.data["results"]: From 756e1f93642d47e8e96cdfb55b9d1b51f09b06b9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 12 Apr 2024 16:42:29 +0100 Subject: [PATCH 06/30] Deactivate workfile instance. --- client/ayon_core/hosts/nuke/api/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 94582f75f1..8f0dfd0713 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -235,6 +235,7 @@ def _submit_headless_farm(node): for _instance in context: if _instance.data["family"] == "workfile": instance_workfile = _instance + _instance.data["active"] = False continue instance_node = _instance.data["transientData"]["node"] From 4fcab3ca36d1efc6d2e56c2ec89c72184a19e1d8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 15 Apr 2024 10:26:22 +0100 Subject: [PATCH 07/30] Remove redundant code. --- client/ayon_core/hosts/nuke/api/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 8f0dfd0713..e608863648 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -254,7 +254,6 @@ def _submit_headless_farm(node): # Enable for farm publishing. instance.data["farm"] = True - instance.data["transfer"] = False # Clear the families as we only want the main family, ei. no review etc. instance.data["families"] = [] From b6a5eadf96828d3a84ff680ccbd590ce90ea11f6 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 15 Apr 2024 12:26:22 +0100 Subject: [PATCH 08/30] Update client/ayon_core/hosts/nuke/api/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hosts/nuke/api/lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 63e6ddef0f..aa44f7c98c 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1163,8 +1163,10 @@ def create_write_node( if char in special_characters: found_special_characters.append(char) - msg = f"Special characters found in name \"{name}\": " - msg += f"{' '.join(found_special_characters)}" + msg = ( + f"Special characters found in name \"{name}\": " + f"{' '.join(found_special_characters)}" + ) assert not found_special_characters, msg prenodes = prenodes or [] From acacf15723e8cf99fcc3cbe194e49612b6dcb53d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 30 Apr 2024 15:24:34 +0100 Subject: [PATCH 09/30] Code cosmetics --- .../hosts/nuke/plugins/create/create_write_render.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py index 16bce64ec6..5340fbdecc 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py @@ -41,14 +41,16 @@ def get_pre_create_attr_defs(self): def create_instance_node(self, product_name, instance_data): settings = self.project_settings["nuke"]["create"]["CreateWriteRender"] - settings = settings["instance_attributes"] + instance_attributes = settings["instance_attributes"] # add fpath_template write_data = { "creator": self.__class__.__name__, "productName": product_name, "fpath_template": self.temp_rendering_path_template, - "headless_farm_submission": "headless_farm_submission" in settings + "headless_farm_submission": ( + "headless_farm_submission" in instance_attributes + ) } write_data.update(instance_data) From 0462d38445f9b4a66635e0ba3bcd9dfd4cf28a46 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 May 2024 15:11:56 +0100 Subject: [PATCH 10/30] Use pyblish plugins instead of code outside of publishing. --- client/ayon_core/hosts/nuke/api/utils.py | 105 ++---------------- .../plugins/publish/collect_headless_farm.py | 41 +++++++ .../plugins/publish/extract_headless_farm.py | 35 ++++++ 3 files changed, 87 insertions(+), 94 deletions(-) create mode 100644 client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py create mode 100644 client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index e608863648..a5d9bfb323 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -1,17 +1,16 @@ import os import re import traceback -from datetime import datetime -import shutil import nuke -from pyblish import util +from pyblish import util, api from qtpy import QtWidgets from ayon_core import resources from ayon_core.pipeline import registered_host from ayon_core.tools.utils import show_message_dialog +from ayon_core.pipeline.create import CreateContext def set_context_favorites(favorites=None): @@ -219,103 +218,21 @@ def _submit_headless_farm(node): node (Node): The node for which the farm submission is being made. """ - context = util.collect() - - success, error_report = create_error_report(context) - - if not success: - show_message_dialog( - "Collection Errors", error_report, level="critical" - ) - return - - # Find instance for node and workfile. - instance = None - instance_workfile = None - for _instance in context: - if _instance.data["family"] == "workfile": - instance_workfile = _instance - _instance.data["active"] = False - continue - - instance_node = _instance.data["transientData"]["node"] - if node.name() == instance_node.name(): - instance = _instance - else: - _instance.data["active"] = False - - if instance is None: - show_message_dialog( - "Collection Error", - "Could not find the instance from the node.", - level="critical" - ) - return - - # Enable for farm publishing. - instance.data["farm"] = True - - # Clear the families as we only want the main family, ei. no review etc. - instance.data["families"] = [] - - # Use the workfile instead of published. - publish_attributes = instance.data["publish_attributes"] - publish_attributes["NukeSubmitDeadline"]["use_published_workfile"] = False - - # Disable version validation. - instance.data.pop("latestVersion") - instance_workfile.data.pop("latestVersion") - - # Validate - util.validate(context) - - success, error_report = create_error_report(context) - - if not success: - show_message_dialog( - "Validation Errors", error_report, level="critical" - ) - return - - # Extraction. - util.extract(context) - - success, error_report = create_error_report(context) - - if not success: - show_message_dialog( - "Extraction Errors", error_report, level="critical" - ) - return - - # Copy the workfile to a timestamped copy. host = registered_host() - current_datetime = datetime.now() - formatted_timestamp = current_datetime.strftime("%Y%m%d%H%M%S") - base, ext = os.path.splitext(host.current_file()) - - directory = os.path.join(os.path.dirname(base), "farm_submissions") - if not os.path.exists(directory): - os.makedirs(directory) - - filename = "{}_{}{}".format( - os.path.basename(base), formatted_timestamp, ext - ) - path = os.path.join(directory, filename).replace("\\", "/") - context.data["currentFile"] = path - shutil.copy(host.current_file(), path) + create_context = CreateContext(host) + context = api.Context() + context.data["create_context"] = create_context + # Used in pyblish plugin to determine which instance to publish. + context.data["node_name"] = node.name() + # Used in pyblish plugins to determine whether to run or not. + context.data["headless_farm"] = True - # Continue to submission. - util.integrate(context) + context = util.publish(context) success, error_report = create_error_report(context) if not success: show_message_dialog( - "Extraction Errors", error_report, level="critical" + "Collection Errors", error_report, level="critical" ) return - - show_message_dialog( - "Submission Successful", "Submission to the farm was successful." - ) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py new file mode 100644 index 0000000000..9bcdd199f3 --- /dev/null +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -0,0 +1,41 @@ +import pyblish.api + + +class CollectHeadlessFarm(pyblish.api.InstancePlugin): + """Setup instances for headless farm submission.""" + + order = pyblish.api.CollectorOrder + 0.4999 + label = "Collect Headless Farm" + hosts = ["nuke"] + + def process(self, instance): + if not instance.context.data.get("headless_farm", False): + return + + if instance.data["family"] == "workfile": + instance.data["active"] = False + + # Disable version validation. + instance.data.pop("latestVersion") + return + + # Filter out all other instances. + node = instance.data["transientData"]["node"] + if node.name() != instance.context.data["node_name"]: + instance.data["active"] = False + return + + # Enable for farm publishing. + instance.data["farm"] = True + + # Clear the families as we only want the main family, ei. no review + # etc. + instance.data["families"] = [] + + # Use the workfile instead of published. + settings = instance.data["publish_attributes"] + settings = settings["NukeSubmitDeadline"] + settings["use_published_workfile"] = False + + # Disable version validation. + instance.data.pop("latestVersion") diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py new file mode 100644 index 0000000000..be74a05392 --- /dev/null +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py @@ -0,0 +1,35 @@ +import os +from datetime import datetime +import shutil + +import pyblish.api + +from ayon_core.pipeline import registered_host + + +class ExtractHeadlessFarm(pyblish.api.InstancePlugin): + """Copy the workfile to a timestamped copy.""" + + order = pyblish.api.ExtractorOrder + 0.499 + label = "Extract Headless Farm" + hosts = ["nuke"] + + def process(self, instance): + if not instance.context.data.get("headless_farm", False): + return + + host = registered_host() + current_datetime = datetime.now() + formatted_timestamp = current_datetime.strftime("%Y%m%d%H%M%S") + base, ext = os.path.splitext(host.current_file()) + + directory = os.path.join(os.path.dirname(base), "farm_submissions") + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "{}_{}{}".format( + os.path.basename(base), formatted_timestamp, ext + ) + path = os.path.join(directory, filename).replace("\\", "/") + instance.context.data["currentFile"] = path + shutil.copy(host.current_file(), path) From 81c75841db70d5ac601fc86f18e003f44d4ec11d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 May 2024 15:14:04 +0100 Subject: [PATCH 11/30] increment nuke package --- server_addon/nuke/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/nuke/package.py b/server_addon/nuke/package.py index bf03c4e7e7..e522b9fb5d 100644 --- a/server_addon/nuke/package.py +++ b/server_addon/nuke/package.py @@ -1,3 +1,3 @@ name = "nuke" title = "Nuke" -version = "0.1.11" +version = "0.1.12" From a3c2bb1415b320fad1eabcc617c5d6643e719f8d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 May 2024 15:52:38 +0100 Subject: [PATCH 12/30] Show successfull message. --- client/ayon_core/hosts/nuke/api/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index a5d9bfb323..5ab2e15552 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -236,3 +236,7 @@ def _submit_headless_farm(node): "Collection Errors", error_report, level="critical" ) return + + show_message_dialog( + "Submission Successful", "Submission to the farm was successful." + ) From e5fbb20bdc51a1c7bb8c9707cd3f659ce26e583d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 May 2024 15:52:52 +0100 Subject: [PATCH 13/30] Skip script version increment --- .../hosts/nuke/plugins/publish/increment_script_version.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py b/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py index 6b0be42ba1..f20748b034 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py @@ -13,6 +13,8 @@ class IncrementScriptVersion(pyblish.api.ContextPlugin): hosts = ['nuke'] def process(self, context): + if context.data.get("headless_farm", False): + return assert all(result["success"] for result in context.data["results"]), ( "Publishing not successful so version is not increased.") From ce13e8629fa63c0b6649061400eccfc09d18fcb1 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 3 May 2024 09:49:52 +0100 Subject: [PATCH 14/30] Full imports --- client/ayon_core/hosts/nuke/api/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 5ab2e15552..8eb0339a89 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -4,7 +4,8 @@ import nuke -from pyblish import util, api +import pyblish.util +import pyblish.api from qtpy import QtWidgets from ayon_core import resources @@ -220,14 +221,14 @@ def _submit_headless_farm(node): host = registered_host() create_context = CreateContext(host) - context = api.Context() + context = pyblish.api.Context() context.data["create_context"] = create_context # Used in pyblish plugin to determine which instance to publish. context.data["node_name"] = node.name() # Used in pyblish plugins to determine whether to run or not. context.data["headless_farm"] = True - context = util.publish(context) + context = pyblish.util.publish(context) success, error_report = create_error_report(context) From 8a971f393c3a2d2f9cecb6afbd910d8f17a76d25 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 May 2024 09:25:31 +0100 Subject: [PATCH 15/30] CollectHeadlessFarm > ContextPlugin --- .../plugins/publish/collect_headless_farm.py | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index 9bcdd199f3..b9b5acf0bc 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -1,41 +1,42 @@ import pyblish.api -class CollectHeadlessFarm(pyblish.api.InstancePlugin): +class CollectHeadlessFarm(pyblish.api.ContextPlugin): """Setup instances for headless farm submission.""" order = pyblish.api.CollectorOrder + 0.4999 label = "Collect Headless Farm" hosts = ["nuke"] - def process(self, instance): - if not instance.context.data.get("headless_farm", False): - return + def process(self, context): + for instance in context: + if not instance.context.data.get("headless_farm", False): + continue - if instance.data["family"] == "workfile": - instance.data["active"] = False + if instance.data["family"] == "workfile": + instance.data["active"] = False - # Disable version validation. - instance.data.pop("latestVersion") - return + # Disable version validation. + instance.data.pop("latestVersion") + continue - # Filter out all other instances. - node = instance.data["transientData"]["node"] - if node.name() != instance.context.data["node_name"]: - instance.data["active"] = False - return + # Filter out all other instances. + node = instance.data["transientData"]["node"] + if node.name() != instance.context.data["node_name"]: + instance.data["active"] = False + continue - # Enable for farm publishing. - instance.data["farm"] = True + # Enable for farm publishing. + instance.data["farm"] = True - # Clear the families as we only want the main family, ei. no review - # etc. - instance.data["families"] = [] + # Clear the families as we only want the main family, ei. no review + # etc. + instance.data["families"] = [] - # Use the workfile instead of published. - settings = instance.data["publish_attributes"] - settings = settings["NukeSubmitDeadline"] - settings["use_published_workfile"] = False + # Use the workfile instead of published. + settings = instance.data["publish_attributes"] + settings = settings["NukeSubmitDeadline"] + settings["use_published_workfile"] = False - # Disable version validation. - instance.data.pop("latestVersion") + # Disable version validation. + instance.data.pop("latestVersion") From 978e7d1be5388fcf3f6bde665917913599f63e7f Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 7 May 2024 09:46:44 +0100 Subject: [PATCH 16/30] Update client/ayon_core/hosts/nuke/api/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hosts/nuke/api/lib.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 2703307400..868c0ada34 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1155,13 +1155,9 @@ def create_write_node( node (obj): group node with avalon data as Knobs ''' # Ensure name does not contain any invalid characters. - special_characters = set("!@#$%^&*()=[]{}|\\;',.<>/?~+-") - found_special_characters = [] - - # Check each character in the node name - for char in name: - if char in special_characters: - found_special_characters.append(char) + special_chars = re.escape("!@#$%^&*()=[]{}|\\;',.<>/?~+-") + special_chars_regex = re.compile(f"[{special_chars}]") + found_special_characters = list(special_chars_regex.findall(name)) msg = ( f"Special characters found in name \"{name}\": " From e9dc1d4a05efb9c03459da3d652f6806e63f6120 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 7 May 2024 09:48:58 +0100 Subject: [PATCH 17/30] Update client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../hosts/nuke/plugins/publish/collect_headless_farm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index b9b5acf0bc..73d2450351 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -9,10 +9,10 @@ class CollectHeadlessFarm(pyblish.api.ContextPlugin): hosts = ["nuke"] def process(self, context): - for instance in context: - if not instance.context.data.get("headless_farm", False): - continue + if not context.data.get("headless_farm", False): + return + for instance in context: if instance.data["family"] == "workfile": instance.data["active"] = False From 72116cd976f6fa5ccab07b902cf3a6e60d7d3700 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 7 May 2024 09:51:42 +0100 Subject: [PATCH 18/30] Update client/ayon_core/hosts/nuke/api/utils.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hosts/nuke/api/utils.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 8eb0339a89..3752028c91 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -173,26 +173,9 @@ def create_error_report(context): success = False - err = result["error"] - formatted_traceback = "".join( - traceback.format_exception( - type(err), - err, - err.__traceback__ - ) - ) - fname = result["plugin"].__module__ - if 'File "", line' in formatted_traceback: - _, lineno, func, msg = err.traceback - fname = os.path.abspath(fname) - formatted_traceback = formatted_traceback.replace( - 'File "", line', - 'File "{0}", line'.format(fname) - ) - err = result["error"] error_message += "\n" - error_message += formatted_traceback + error_message += err.formatted_traceback return success, error_message From 5b591b602fe8119f68d7cf4656812c5c0cc2911f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 May 2024 10:07:27 +0100 Subject: [PATCH 19/30] Ensure CreateInstance is active. --- client/ayon_core/hosts/nuke/api/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 3752028c91..08e2630cbd 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -204,6 +204,14 @@ def _submit_headless_farm(node): host = registered_host() create_context = CreateContext(host) + + # Ensure CreateInstance is enabled. + for instance in create_context.instances: + if node.name() != instance.transient_data["node"].name(): + continue + + instance.data["active"] = True + context = pyblish.api.Context() context.data["create_context"] = create_context # Used in pyblish plugin to determine which instance to publish. From 54500dbb59d9e6c4ab0ad5625f568aea64c01548 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 7 May 2024 10:08:29 +0100 Subject: [PATCH 20/30] Update client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../hosts/nuke/plugins/publish/collect_headless_farm.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index 73d2450351..4bdfc28fe9 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -15,9 +15,6 @@ def process(self, context): for instance in context: if instance.data["family"] == "workfile": instance.data["active"] = False - - # Disable version validation. - instance.data.pop("latestVersion") continue # Filter out all other instances. From fd71818d4683b47a3aa43a61e2323fd570dbd5e3 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 May 2024 10:11:54 +0100 Subject: [PATCH 21/30] Remove redundant code --- .../hosts/nuke/plugins/publish/collect_headless_farm.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index 4bdfc28fe9..f2af3551d9 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -34,6 +34,3 @@ def process(self, context): settings = instance.data["publish_attributes"] settings = settings["NukeSubmitDeadline"] settings["use_published_workfile"] = False - - # Disable version validation. - instance.data.pop("latestVersion") From d8eb451887e17ad3eb3ca794d29d8f442159cc62 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 May 2024 10:30:55 +0100 Subject: [PATCH 22/30] Illicit feedback --- .../hosts/nuke/plugins/publish/collect_headless_farm.py | 6 ++---- .../hosts/nuke/plugins/publish/extract_headless_farm.py | 1 + .../deadline/plugins/publish/submit_nuke_deadline.py | 7 +++++-- client/ayon_core/plugins/publish/validate_version.py | 9 +++++++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index f2af3551d9..dfd294cebc 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -28,9 +28,7 @@ def process(self, context): # Clear the families as we only want the main family, ei. no review # etc. - instance.data["families"] = [] + instance.data["families"] = ["headless_farm"] # Use the workfile instead of published. - settings = instance.data["publish_attributes"] - settings = settings["NukeSubmitDeadline"] - settings["use_published_workfile"] = False + instance.data["use_published_workfile"] = False diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py index be74a05392..003e51aa1a 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py @@ -13,6 +13,7 @@ class ExtractHeadlessFarm(pyblish.api.InstancePlugin): order = pyblish.api.ExtractorOrder + 0.499 label = "Extract Headless Farm" hosts = ["nuke"] + families = ["headless_farm"] def process(self, instance): if not instance.context.data.get("headless_farm", False): diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py index d70cb75bf3..751fb4c46a 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -128,8 +128,11 @@ def process(self, instance): render_path = instance.data['path'] script_path = context.data["currentFile"] - use_published_workfile = instance.data["attributeValues"].get( - "use_published_workfile", self.use_published_workfile + use_published_workfile = instance.data.get( + "use_published_workfile", + instance.data["attributeValues"].get( + "use_published_workfile", self.use_published_workfile + ) ) if use_published_workfile: script_path = self._get_published_workfile_path(context) diff --git a/client/ayon_core/plugins/publish/validate_version.py b/client/ayon_core/plugins/publish/validate_version.py index 9031194e8c..25a5757330 100644 --- a/client/ayon_core/plugins/publish/validate_version.py +++ b/client/ayon_core/plugins/publish/validate_version.py @@ -1,8 +1,10 @@ import pyblish.api -from ayon_core.pipeline.publish import PublishValidationError +from ayon_core.pipeline.publish import ( + PublishValidationError, OptionalPyblishPluginMixin +) -class ValidateVersion(pyblish.api.InstancePlugin): +class ValidateVersion(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate instance version. AYON does not allow overwriting previously published versions. @@ -18,6 +20,9 @@ class ValidateVersion(pyblish.api.InstancePlugin): active = True def process(self, instance): + if not self.is_active(instance.data): + return + version = instance.data.get("version") latest_version = instance.data.get("latestVersion") From ad7d24c5cfbba3d27f14a69b167ec71b8bc6e819 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 May 2024 12:09:20 +0100 Subject: [PATCH 23/30] Disable instances as early as possible. --- .../plugins/publish/collect_headless_farm.py | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index dfd294cebc..5a3d3cc0de 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -4,7 +4,8 @@ class CollectHeadlessFarm(pyblish.api.ContextPlugin): """Setup instances for headless farm submission.""" - order = pyblish.api.CollectorOrder + 0.4999 + # Needs to be after CollectFromCreateContext + order = pyblish.api.CollectorOrder - 0.4 label = "Collect Headless Farm" hosts = ["nuke"] @@ -23,12 +24,24 @@ def process(self, context): instance.data["active"] = False continue - # Enable for farm publishing. - instance.data["farm"] = True + instance.data["families"].append("headless_farm") + + +class SetupHeadlessFarm(pyblish.api.InstancePlugin): + """Setup instance for headless farm submission.""" + + order = pyblish.api.CollectorOrder + 0.4999 + label = "Setup Headless Farm" + hosts = ["nuke"] + families = ["headless_farm"] + + def process(self, instance): + # Enable for farm publishing. + instance.data["farm"] = True - # Clear the families as we only want the main family, ei. no review - # etc. - instance.data["families"] = ["headless_farm"] + # Clear the families as we only want the main family, ei. no review + # etc. + instance.data["families"] = ["headless_farm"] - # Use the workfile instead of published. - instance.data["use_published_workfile"] = False + # Use the workfile instead of published. + instance.data["use_published_workfile"] = False From a75cb56dc5f31e16184322becd01bc56aa00c7d8 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 23 May 2024 16:16:07 +0100 Subject: [PATCH 24/30] Update client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../hosts/nuke/plugins/publish/collect_headless_farm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index 5a3d3cc0de..e59c296904 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -44,4 +44,5 @@ def process(self, instance): instance.data["families"] = ["headless_farm"] # Use the workfile instead of published. - instance.data["use_published_workfile"] = False + attribute_values = instance.data.setdefault("attributeValues", {}) + attribute_values["use_published_workfile"] = False From 459e9a51c04542da277d80c110c86a06a63bae8e Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 23 May 2024 16:17:33 +0100 Subject: [PATCH 25/30] Update client/ayon_core/hosts/nuke/api/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hosts/nuke/api/lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index 9acd8ecfa9..f1a9418111 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1027,8 +1027,10 @@ def script_name(): def add_button_headless_farm_submission(node): name = "headlessFarmSubmission" label = "Headless Farm Submission" - value = "from ayon_core.hosts.nuke.api.utils import submit_headless_farm;" - value += "submit_headless_farm(nuke.thisNode())" + value = ( + "from ayon_core.hosts.nuke.api.utils import submit_headless_farm;" + "submit_headless_farm(nuke.thisNode())" + ) knob = nuke.PyScript_Knob(name, label, value) knob.clearFlag(nuke.STARTLINE) node.addKnob(knob) From 3debb92c02f7ed539983f734821bfdda7512e385 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 May 2024 17:15:52 +0100 Subject: [PATCH 26/30] Use publish_attributes --- .../nuke/plugins/publish/collect_headless_farm.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index e59c296904..7db2ed117c 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -1,5 +1,9 @@ import pyblish.api +from ayon_core.pipeline.publish import ( + AYONPyblishPluginMixin +) + class CollectHeadlessFarm(pyblish.api.ContextPlugin): """Setup instances for headless farm submission.""" @@ -27,7 +31,7 @@ def process(self, context): instance.data["families"].append("headless_farm") -class SetupHeadlessFarm(pyblish.api.InstancePlugin): +class SetupHeadlessFarm(pyblish.api.InstancePlugin, AYONPyblishPluginMixin): """Setup instance for headless farm submission.""" order = pyblish.api.CollectorOrder + 0.4999 @@ -44,5 +48,6 @@ def process(self, instance): instance.data["families"] = ["headless_farm"] # Use the workfile instead of published. - attribute_values = instance.data.setdefault("attributeValues", {}) - attribute_values["use_published_workfile"] = False + publish_attributes = instance.data["publish_attributes"] + plugin_attributes = publish_attributes["NukeSubmitDeadline"] + plugin_attributes["use_published_workfile"] = False From 4c6eb7a84ddff30e7bb028ac2ae4473202df545e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 May 2024 17:16:39 +0100 Subject: [PATCH 27/30] Revert use_published_workfile --- .../deadline/plugins/publish/submit_nuke_deadline.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py index 6e752a5455..db35c2ae67 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -115,11 +115,8 @@ def process(self, instance): render_path = instance.data['path'] script_path = context.data["currentFile"] - use_published_workfile = instance.data.get( - "use_published_workfile", - instance.data["attributeValues"].get( - "use_published_workfile", self.use_published_workfile - ) + use_published_workfile = instance.data["attributeValues"].get( + "use_published_workfile", self.use_published_workfile ) if use_published_workfile: script_path = self._get_published_workfile_path(context) From 23ee1caa44ea702212a04b08899757a63a004618 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 23 May 2024 17:22:00 +0100 Subject: [PATCH 28/30] Update client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../hosts/nuke/plugins/publish/collect_headless_farm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index 7db2ed117c..82b6b2b3e9 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -9,7 +9,7 @@ class CollectHeadlessFarm(pyblish.api.ContextPlugin): """Setup instances for headless farm submission.""" # Needs to be after CollectFromCreateContext - order = pyblish.api.CollectorOrder - 0.4 + order = pyblish.api.CollectorOrder - 0.49 label = "Collect Headless Farm" hosts = ["nuke"] From 3dfe36702904dd9575d1314410a4c1aaca055bd5 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 May 2024 22:40:33 +0100 Subject: [PATCH 29/30] Change to Render On Farm --- client/ayon_core/hosts/nuke/api/lib.py | 14 ++--- client/ayon_core/hosts/nuke/api/utils.py | 56 ++++++------------- .../nuke/plugins/create/create_write_image.py | 5 +- .../plugins/create/create_write_prerender.py | 6 +- .../plugins/create/create_write_render.py | 5 +- .../plugins/publish/collect_headless_farm.py | 29 +++++----- .../plugins/publish/extract_headless_farm.py | 8 +-- .../publish/increment_script_version.py | 2 +- server_addon/nuke/package.py | 2 +- .../nuke/server/settings/create_plugins.py | 4 +- 10 files changed, 58 insertions(+), 73 deletions(-) diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/client/ayon_core/hosts/nuke/api/lib.py index f1a9418111..500a0f9601 100644 --- a/client/ayon_core/hosts/nuke/api/lib.py +++ b/client/ayon_core/hosts/nuke/api/lib.py @@ -1024,12 +1024,12 @@ def script_name(): return nuke.root().knob("name").value() -def add_button_headless_farm_submission(node): - name = "headlessFarmSubmission" - label = "Headless Farm Submission" +def add_button_render_on_farm(node): + name = "renderOnFarm" + label = "Render On Farm" value = ( - "from ayon_core.hosts.nuke.api.utils import submit_headless_farm;" - "submit_headless_farm(nuke.thisNode())" + "from ayon_core.hosts.nuke.api.utils import submit_render_on_farm;" + "submit_render_on_farm(nuke.thisNode())" ) knob = nuke.PyScript_Knob(name, label, value) knob.clearFlag(nuke.STARTLINE) @@ -1294,8 +1294,8 @@ def create_write_node( GN.addKnob(link) # Adding render farm submission button. - if data.get("headless_farm_submission", False): - add_button_headless_farm_submission(GN) + if data.get("render_on_farm", False): + add_button_render_on_farm(GN) # adding write to read button add_button_write_to_read(GN) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 08e2630cbd..1c9b0b8996 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -1,6 +1,5 @@ import os import re -import traceback import nuke @@ -151,48 +150,19 @@ def is_headless(): return QtWidgets.QApplication.instance() is None -def create_error_report(context): - """Create an error report based on the given pyblish context. - - This function iterates through the results in the context and formats any - errors into a comprehensive error report. - - Args: - context (dict): Pyblish context. - - Returns: - tuple: A tuple containing a boolean indicating success and a string - representing the error message. - """ - - error_message = "" - success = True - for result in context.data["results"]: - if result["success"]: - continue - - success = False - - err = result["error"] - error_message += "\n" - error_message += err.formatted_traceback - - return success, error_message - - -def submit_headless_farm(node): +def submit_render_on_farm(node): # Ensure code is executed in root context. if nuke.root() == nuke.thisNode(): - _submit_headless_farm(node) + _submit_render_on_farm(node) else: # If not in root context, move to the root context and then execute the # code. with nuke.root(): - _submit_headless_farm(node) + _submit_render_on_farm(node) -def _submit_headless_farm(node): - """Headless farm submission +def _submit_render_on_farm(node): + """Render on farm submission This function prepares the context for farm submission, validates it, extracts relevant data, copies the current workfile to a timestamped copy, @@ -217,15 +187,25 @@ def _submit_headless_farm(node): # Used in pyblish plugin to determine which instance to publish. context.data["node_name"] = node.name() # Used in pyblish plugins to determine whether to run or not. - context.data["headless_farm"] = True + context.data["render_on_farm"] = True context = pyblish.util.publish(context) - success, error_report = create_error_report(context) + error_message = "" + success = True + for result in context.data["results"]: + if result["success"]: + continue + + success = False + + err = result["error"] + error_message += "\n" + error_message += err.formatted_traceback if not success: show_message_dialog( - "Collection Errors", error_report, level="critical" + "Publish Errors", error_message, level="critical" ) return diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py index 046b99f6b0..fc2538f23d 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py @@ -66,14 +66,15 @@ def _get_frame_source_number(self): def create_instance_node(self, product_name, instance_data): settings = self.project_settings["nuke"]["create"]["CreateWriteImage"] - settings = settings["instance_attributes"] # add fpath_template write_data = { "creator": self.__class__.__name__, "productName": product_name, "fpath_template": self.temp_rendering_path_template, - "headless_farm_submission": "headless_farm_submission" in settings + "render_on_farm": ( + "render_on_farm" in settings["instance_attributes"] + ) } write_data.update(instance_data) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py index df906c9c25..47796d159c 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py @@ -47,14 +47,16 @@ def get_pre_create_attr_defs(self): def create_instance_node(self, product_name, instance_data): settings = self.project_settings["nuke"]["create"] - settings = settings["CreateWritePrerender"]["instance_attributes"] + settings = settings["CreateWritePrerender"] # add fpath_template write_data = { "creator": self.__class__.__name__, "productName": product_name, "fpath_template": self.temp_rendering_path_template, - "headless_farm_submission": "headless_farm_submission" in settings + "render_on_farm": ( + "render_on_farm" in settings["instance_attributes"] + ) } write_data.update(instance_data) diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py index 5340fbdecc..4cb5ccdfa2 100644 --- a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py +++ b/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py @@ -41,15 +41,14 @@ def get_pre_create_attr_defs(self): def create_instance_node(self, product_name, instance_data): settings = self.project_settings["nuke"]["create"]["CreateWriteRender"] - instance_attributes = settings["instance_attributes"] # add fpath_template write_data = { "creator": self.__class__.__name__, "productName": product_name, "fpath_template": self.temp_rendering_path_template, - "headless_farm_submission": ( - "headless_farm_submission" in instance_attributes + "render_on_farm": ( + "render_on_farm" in settings["instance_attributes"] ) } diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py index 82b6b2b3e9..3f49a2bf01 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/collect_headless_farm.py @@ -5,16 +5,16 @@ ) -class CollectHeadlessFarm(pyblish.api.ContextPlugin): - """Setup instances for headless farm submission.""" +class CollectRenderOnFarm(pyblish.api.ContextPlugin): + """Setup instances for render on farm submission.""" # Needs to be after CollectFromCreateContext order = pyblish.api.CollectorOrder - 0.49 - label = "Collect Headless Farm" + label = "Collect Render On Farm" hosts = ["nuke"] def process(self, context): - if not context.data.get("headless_farm", False): + if not context.data.get("render_on_farm", False): return for instance in context: @@ -28,24 +28,27 @@ def process(self, context): instance.data["active"] = False continue - instance.data["families"].append("headless_farm") + instance.data["families"].append("render_on_farm") + # Enable for farm publishing. + instance.data["farm"] = True -class SetupHeadlessFarm(pyblish.api.InstancePlugin, AYONPyblishPluginMixin): - """Setup instance for headless farm submission.""" + # Skip workfile version incremental save. + instance.context.data["increment_script_version"] = False + + +class SetupRenderOnFarm(pyblish.api.InstancePlugin, AYONPyblishPluginMixin): + """Setup instance for render on farm submission.""" order = pyblish.api.CollectorOrder + 0.4999 - label = "Setup Headless Farm" + label = "Setup Render On Farm" hosts = ["nuke"] - families = ["headless_farm"] + families = ["render_on_farm"] def process(self, instance): - # Enable for farm publishing. - instance.data["farm"] = True - # Clear the families as we only want the main family, ei. no review # etc. - instance.data["families"] = ["headless_farm"] + instance.data["families"] = ["render_on_farm"] # Use the workfile instead of published. publish_attributes = instance.data["publish_attributes"] diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py b/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py index 003e51aa1a..4ba55f8c46 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/extract_headless_farm.py @@ -7,16 +7,16 @@ from ayon_core.pipeline import registered_host -class ExtractHeadlessFarm(pyblish.api.InstancePlugin): +class ExtractRenderOnFarm(pyblish.api.InstancePlugin): """Copy the workfile to a timestamped copy.""" order = pyblish.api.ExtractorOrder + 0.499 - label = "Extract Headless Farm" + label = "Extract Render On Farm" hosts = ["nuke"] - families = ["headless_farm"] + families = ["render_on_farm"] def process(self, instance): - if not instance.context.data.get("headless_farm", False): + if not instance.context.data.get("render_on_farm", False): return host = registered_host() diff --git a/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py b/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py index f20748b034..70fd04a985 100644 --- a/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py +++ b/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py @@ -13,7 +13,7 @@ class IncrementScriptVersion(pyblish.api.ContextPlugin): hosts = ['nuke'] def process(self, context): - if context.data.get("headless_farm", False): + if not context.data.get("increment_script_version", True): return assert all(result["success"] for result in context.data["results"]), ( diff --git a/server_addon/nuke/package.py b/server_addon/nuke/package.py index bc166bd14e..d8decef208 100644 --- a/server_addon/nuke/package.py +++ b/server_addon/nuke/package.py @@ -1,3 +1,3 @@ name = "nuke" title = "Nuke" -version = "0.1.13" +version = "0.1.14" diff --git a/server_addon/nuke/server/settings/create_plugins.py b/server_addon/nuke/server/settings/create_plugins.py index 897a467118..e4a0f9c938 100644 --- a/server_addon/nuke/server/settings/create_plugins.py +++ b/server_addon/nuke/server/settings/create_plugins.py @@ -14,8 +14,8 @@ def instance_attributes_enum(): {"value": "farm_rendering", "label": "Farm rendering"}, {"value": "use_range_limit", "label": "Use range limit"}, { - "value": "headless_farm_submission", - "label": "Headless Farm Submission" + "value": "render_on_farm", + "label": "Render On Farm" } ] From da4da46c9cd5600f56ded8bf5a3985b488796e83 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 May 2024 15:28:02 +0200 Subject: [PATCH 30/30] Blacklisting plugins for workfile version validation and incrementing Refactored render submission process to handle plugins differently, bypassing version validation and incrementing by removing specific plugins from the list. --- client/ayon_core/hosts/nuke/api/utils.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/client/ayon_core/hosts/nuke/api/utils.py index 1c9b0b8996..646bb0ece1 100644 --- a/client/ayon_core/hosts/nuke/api/utils.py +++ b/client/ayon_core/hosts/nuke/api/utils.py @@ -189,7 +189,17 @@ def _submit_render_on_farm(node): # Used in pyblish plugins to determine whether to run or not. context.data["render_on_farm"] = True - context = pyblish.util.publish(context) + # Since we need to bypass version validation and incrementing, we need to + # remove the plugins from the list that are responsible for these tasks. + plugins = pyblish.api.discover() + blacklist = ["IncrementScriptVersion", "ValidateVersion"] + plugins = [ + plugin + for plugin in plugins + if plugin.__name__ not in blacklist + ] + + context = pyblish.util.publish(context, plugins=plugins) error_message = "" success = True