From 65a77361a9285c150e049d3e0b2c3f3f7946e86d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 9 Nov 2021 12:39:59 +0100 Subject: [PATCH 01/27] adding baking colorspace knob to render nodes --- openpype/hosts/nuke/api/lib.py | 397 ++------------------------------- 1 file changed, 17 insertions(+), 380 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 9ee3a4464b..e1e417df4d 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -478,6 +478,7 @@ def create_write_node(name, data, input=None, prenodes=None, if review: add_review_knob(GN) + add_bake_colorspace_knob(GN) # add divider GN.addKnob(nuke.Text_Knob('', 'Rendering')) @@ -571,6 +572,22 @@ def add_review_knob(node): return node +def add_bake_colorspace_knob(node): + ''' Adds additional bake colorspace knob to given node + + Arguments: + node (obj): nuke node object to be fixed + + Return: + node (obj): with added knob + ''' + if "bake_colorspace" not in node.knobs(): + knob = nuke.Boolean_Knob("bake_colorspace", "Bake colorspace") + knob.setValue(True) + node.addKnob(knob) + return node + + def add_deadline_tab(node): node.addKnob(nuke.Tab_Knob("Deadline")) @@ -1160,386 +1177,6 @@ def get_write_node_template_attr(node): return anlib.fix_data_for_node_create(correct_data) -class ExporterReview: - """ - Base class object for generating review data from Nuke - - Args: - klass (pyblish.plugin): pyblish plugin parent - instance (pyblish.instance): instance of pyblish context - - """ - _temp_nodes = [] - data = dict({ - "representations": list() - }) - - def __init__(self, - klass, - instance - ): - - self.log = klass.log - self.instance = instance - self.path_in = self.instance.data.get("path", None) - self.staging_dir = self.instance.data["stagingDir"] - self.collection = self.instance.data.get("collection", None) - - def get_file_info(self): - if self.collection: - self.log.debug("Collection: `{}`".format(self.collection)) - # get path - self.fname = os.path.basename(self.collection.format( - "{head}{padding}{tail}")) - self.fhead = self.collection.format("{head}") - - # get first and last frame - self.first_frame = min(self.collection.indexes) - self.last_frame = max(self.collection.indexes) - if "slate" in self.instance.data["families"]: - self.first_frame += 1 - else: - self.fname = os.path.basename(self.path_in) - self.fhead = os.path.splitext(self.fname)[0] + "." - self.first_frame = self.instance.data.get("frameStartHandle", None) - self.last_frame = self.instance.data.get("frameEndHandle", None) - - if "#" in self.fhead: - self.fhead = self.fhead.replace("#", "")[:-1] - - def get_representation_data(self, tags=None, range=False): - add_tags = [] - if tags: - add_tags = tags - - repre = { - 'name': self.name, - 'ext': self.ext, - 'files': self.file, - "stagingDir": self.staging_dir, - "tags": [self.name.replace("_", "-")] + add_tags - } - - if range: - repre.update({ - "frameStart": self.first_frame, - "frameEnd": self.last_frame, - }) - - self.data["representations"].append(repre) - - def get_view_process_node(self): - """ - Will get any active view process. - - Arguments: - self (class): in object definition - - Returns: - nuke.Node: copy node of Input Process node - """ - anlib.reset_selection() - ipn_orig = None - for v in nuke.allNodes(filter="Viewer"): - ip = v['input_process'].getValue() - ipn = v['input_process_node'].getValue() - if "VIEWER_INPUT" not in ipn and ip: - ipn_orig = nuke.toNode(ipn) - ipn_orig.setSelected(True) - - if ipn_orig: - # copy selected to clipboard - nuke.nodeCopy('%clipboard%') - # reset selection - anlib.reset_selection() - # paste node and selection is on it only - nuke.nodePaste('%clipboard%') - # assign to variable - ipn = nuke.selectedNode() - - return ipn - - def clean_nodes(self): - for node in self._temp_nodes: - nuke.delete(node) - self._temp_nodes = [] - self.log.info("Deleted nodes...") - - -class ExporterReviewLut(ExporterReview): - """ - Generator object for review lut from Nuke - - Args: - klass (pyblish.plugin): pyblish plugin parent - instance (pyblish.instance): instance of pyblish context - - - """ - - def __init__(self, - klass, - instance, - name=None, - ext=None, - cube_size=None, - lut_size=None, - lut_style=None): - # initialize parent class - ExporterReview.__init__(self, klass, instance) - self._temp_nodes = [] - - # deal with now lut defined in viewer lut - if hasattr(klass, "viewer_lut_raw"): - self.viewer_lut_raw = klass.viewer_lut_raw - else: - self.viewer_lut_raw = False - - self.name = name or "baked_lut" - self.ext = ext or "cube" - self.cube_size = cube_size or 32 - self.lut_size = lut_size or 1024 - self.lut_style = lut_style or "linear" - - # set frame start / end and file name to self - self.get_file_info() - - self.log.info("File info was set...") - - self.file = self.fhead + self.name + ".{}".format(self.ext) - self.path = os.path.join( - self.staging_dir, self.file).replace("\\", "/") - - def generate_lut(self): - # ---------- start nodes creation - - # CMSTestPattern - cms_node = nuke.createNode("CMSTestPattern") - cms_node["cube_size"].setValue(self.cube_size) - # connect - self._temp_nodes.append(cms_node) - self.previous_node = cms_node - self.log.debug("CMSTestPattern... `{}`".format(self._temp_nodes)) - - # Node View Process - ipn = self.get_view_process_node() - if ipn is not None: - # connect - ipn.setInput(0, self.previous_node) - self._temp_nodes.append(ipn) - self.previous_node = ipn - self.log.debug("ViewProcess... `{}`".format(self._temp_nodes)) - - if not self.viewer_lut_raw: - # OCIODisplay - dag_node = nuke.createNode("OCIODisplay") - # connect - dag_node.setInput(0, self.previous_node) - self._temp_nodes.append(dag_node) - self.previous_node = dag_node - self.log.debug("OCIODisplay... `{}`".format(self._temp_nodes)) - - # GenerateLUT - gen_lut_node = nuke.createNode("GenerateLUT") - gen_lut_node["file"].setValue(self.path) - gen_lut_node["file_type"].setValue(".{}".format(self.ext)) - gen_lut_node["lut1d"].setValue(self.lut_size) - gen_lut_node["style1d"].setValue(self.lut_style) - # connect - gen_lut_node.setInput(0, self.previous_node) - self._temp_nodes.append(gen_lut_node) - self.log.debug("GenerateLUT... `{}`".format(self._temp_nodes)) - - # ---------- end nodes creation - - # Export lut file - nuke.execute( - gen_lut_node.name(), - int(self.first_frame), - int(self.first_frame)) - - self.log.info("Exported...") - - # ---------- generate representation data - self.get_representation_data() - - self.log.debug("Representation... `{}`".format(self.data)) - - # ---------- Clean up - self.clean_nodes() - - return self.data - - -class ExporterReviewMov(ExporterReview): - """ - Metaclass for generating review mov files - - Args: - klass (pyblish.plugin): pyblish plugin parent - instance (pyblish.instance): instance of pyblish context - - """ - - def __init__(self, - klass, - instance, - name=None, - ext=None, - ): - # initialize parent class - ExporterReview.__init__(self, klass, instance) - - # passing presets for nodes to self - if hasattr(klass, "nodes"): - self.nodes = klass.nodes - else: - self.nodes = {} - - # deal with now lut defined in viewer lut - self.viewer_lut_raw = klass.viewer_lut_raw - self.bake_colorspace_fallback = klass.bake_colorspace_fallback - self.bake_colorspace_main = klass.bake_colorspace_main - self.write_colorspace = instance.data["colorspace"] - - self.name = name or "baked" - self.ext = ext or "mov" - - # set frame start / end and file name to self - self.get_file_info() - - self.log.info("File info was set...") - - self.file = self.fhead + self.name + ".{}".format(self.ext) - self.path = os.path.join( - self.staging_dir, self.file).replace("\\", "/") - - def render(self, render_node_name): - self.log.info("Rendering... ") - # Render Write node - nuke.execute( - render_node_name, - int(self.first_frame), - int(self.last_frame)) - - self.log.info("Rendered...") - - def save_file(self): - import shutil - with anlib.maintained_selection(): - self.log.info("Saving nodes as file... ") - # create nk path - path = os.path.splitext(self.path)[0] + ".nk" - # save file to the path - shutil.copyfile(self.instance.context.data["currentFile"], path) - - self.log.info("Nodes exported...") - return path - - def generate_mov(self, farm=False): - # ---------- start nodes creation - - # Read node - r_node = nuke.createNode("Read") - r_node["file"].setValue(self.path_in) - r_node["first"].setValue(self.first_frame) - r_node["origfirst"].setValue(self.first_frame) - r_node["last"].setValue(self.last_frame) - r_node["origlast"].setValue(self.last_frame) - r_node["colorspace"].setValue(self.write_colorspace) - - # connect - self._temp_nodes.append(r_node) - self.previous_node = r_node - self.log.debug("Read... `{}`".format(self._temp_nodes)) - - # View Process node - ipn = self.get_view_process_node() - if ipn is not None: - # connect - ipn.setInput(0, self.previous_node) - self._temp_nodes.append(ipn) - self.previous_node = ipn - self.log.debug("ViewProcess... `{}`".format(self._temp_nodes)) - - if not self.viewer_lut_raw: - colorspaces = [ - self.bake_colorspace_main, self.bake_colorspace_fallback - ] - - if any(colorspaces): - # OCIOColorSpace with controled output - dag_node = nuke.createNode("OCIOColorSpace") - self._temp_nodes.append(dag_node) - for c in colorspaces: - test = dag_node["out_colorspace"].setValue(str(c)) - if test: - self.log.info( - "Baking in colorspace... `{}`".format(c)) - break - - if not test: - dag_node = nuke.createNode("OCIODisplay") - else: - # OCIODisplay - dag_node = nuke.createNode("OCIODisplay") - - # connect - dag_node.setInput(0, self.previous_node) - self._temp_nodes.append(dag_node) - self.previous_node = dag_node - self.log.debug("OCIODisplay... `{}`".format(self._temp_nodes)) - - # Write node - write_node = nuke.createNode("Write") - self.log.debug("Path: {}".format(self.path)) - write_node["file"].setValue(self.path) - write_node["file_type"].setValue(self.ext) - - # Knobs `meta_codec` and `mov64_codec` are not available on centos. - # TODO change this to use conditions, if possible. - try: - write_node["meta_codec"].setValue("ap4h") - except Exception: - self.log.info("`meta_codec` knob was not found") - - try: - write_node["mov64_codec"].setValue("ap4h") - except Exception: - self.log.info("`mov64_codec` knob was not found") - write_node["mov64_write_timecode"].setValue(1) - write_node["raw"].setValue(1) - # connect - write_node.setInput(0, self.previous_node) - self._temp_nodes.append(write_node) - self.log.debug("Write... `{}`".format(self._temp_nodes)) - # ---------- end nodes creation - - # ---------- render or save to nk - if farm: - nuke.scriptSave() - path_nk = self.save_file() - self.data.update({ - "bakeScriptPath": path_nk, - "bakeWriteNodeName": write_node.name(), - "bakeRenderPath": self.path - }) - else: - self.render(write_node.name()) - # ---------- generate representation data - self.get_representation_data( - tags=["review", "delete"], - range=True - ) - - self.log.debug("Representation... `{}`".format(self.data)) - - # ---------- Clean up - self.clean_nodes() - nuke.scriptSave() - return self.data - - def get_dependent_nodes(nodes): """Get all dependent nodes connected to the list of nodes. From 46f808c5bcd7dfde7dd9e4a42cc33e2909d34b25 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 9 Nov 2021 12:40:46 +0100 Subject: [PATCH 02/27] moving plugin based funcs to right module --- openpype/hosts/nuke/api/plugin.py | 385 ++++++++++++++++++ .../publish/extract_review_data_lut.py | 6 +- .../publish/extract_review_data_mov.py | 6 +- 3 files changed, 391 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 62eadecaf4..9801e19126 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -1,3 +1,4 @@ +import os import random import string @@ -94,3 +95,387 @@ class NukeLoader(api.Loader): nuke.delete(member) return dependent_nodes + + + +class ExporterReview: + """ + Base class object for generating review data from Nuke + + Args: + klass (pyblish.plugin): pyblish plugin parent + instance (pyblish.instance): instance of pyblish context + + """ + _temp_nodes = [] + data = dict({ + "representations": list() + }) + + def __init__(self, + klass, + instance + ): + + self.log = klass.log + self.instance = instance + self.bake_colorspace = instance.data["bakeColorspace"] + self.path_in = self.instance.data.get("path", None) + self.staging_dir = self.instance.data["stagingDir"] + self.collection = self.instance.data.get("collection", None) + + def get_file_info(self): + if self.collection: + self.log.debug("Collection: `{}`".format(self.collection)) + # get path + self.fname = os.path.basename(self.collection.format( + "{head}{padding}{tail}")) + self.fhead = self.collection.format("{head}") + + # get first and last frame + self.first_frame = min(self.collection.indexes) + self.last_frame = max(self.collection.indexes) + if "slate" in self.instance.data["families"]: + self.first_frame += 1 + else: + self.fname = os.path.basename(self.path_in) + self.fhead = os.path.splitext(self.fname)[0] + "." + self.first_frame = self.instance.data.get("frameStartHandle", None) + self.last_frame = self.instance.data.get("frameEndHandle", None) + + if "#" in self.fhead: + self.fhead = self.fhead.replace("#", "")[:-1] + + def get_representation_data(self, tags=None, range=False): + add_tags = [] + if tags: + add_tags = tags + + repre = { + 'name': self.name, + 'ext': self.ext, + 'files': self.file, + "stagingDir": self.staging_dir, + "tags": [self.name.replace("_", "-")] + add_tags + } + + if range: + repre.update({ + "frameStart": self.first_frame, + "frameEnd": self.last_frame, + }) + + self.data["representations"].append(repre) + + def get_view_process_node(self): + """ + Will get any active view process. + + Arguments: + self (class): in object definition + + Returns: + nuke.Node: copy node of Input Process node + """ + anlib.reset_selection() + ipn_orig = None + for v in nuke.allNodes(filter="Viewer"): + ip = v['input_process'].getValue() + ipn = v['input_process_node'].getValue() + if "VIEWER_INPUT" not in ipn and ip: + ipn_orig = nuke.toNode(ipn) + ipn_orig.setSelected(True) + + if ipn_orig: + # copy selected to clipboard + nuke.nodeCopy('%clipboard%') + # reset selection + anlib.reset_selection() + # paste node and selection is on it only + nuke.nodePaste('%clipboard%') + # assign to variable + ipn = nuke.selectedNode() + + return ipn + + def clean_nodes(self): + for node in self._temp_nodes: + nuke.delete(node) + self._temp_nodes = [] + self.log.info("Deleted nodes...") + + +class ExporterReviewLut(ExporterReview): + """ + Generator object for review lut from Nuke + + Args: + klass (pyblish.plugin): pyblish plugin parent + instance (pyblish.instance): instance of pyblish context + + + """ + + def __init__(self, + klass, + instance, + name=None, + ext=None, + cube_size=None, + lut_size=None, + lut_style=None): + # initialize parent class + ExporterReview.__init__(self, klass, instance) + self._temp_nodes = [] + + # deal with now lut defined in viewer lut + if hasattr(klass, "viewer_lut_raw"): + self.viewer_lut_raw = klass.viewer_lut_raw + else: + self.viewer_lut_raw = False + + self.name = name or "baked_lut" + self.ext = ext or "cube" + self.cube_size = cube_size or 32 + self.lut_size = lut_size or 1024 + self.lut_style = lut_style or "linear" + + # set frame start / end and file name to self + self.get_file_info() + + self.log.info("File info was set...") + + self.file = self.fhead + self.name + ".{}".format(self.ext) + self.path = os.path.join( + self.staging_dir, self.file).replace("\\", "/") + + def generate_lut(self): + # ---------- start nodes creation + + # CMSTestPattern + cms_node = nuke.createNode("CMSTestPattern") + cms_node["cube_size"].setValue(self.cube_size) + # connect + self._temp_nodes.append(cms_node) + self.previous_node = cms_node + self.log.debug("CMSTestPattern... `{}`".format(self._temp_nodes)) + + # Node View Process + ipn = self.get_view_process_node() + if ipn is not None: + # connect + ipn.setInput(0, self.previous_node) + self._temp_nodes.append(ipn) + self.previous_node = ipn + self.log.debug("ViewProcess... `{}`".format(self._temp_nodes)) + + if not self.viewer_lut_raw: + # OCIODisplay + dag_node = nuke.createNode("OCIODisplay") + # connect + dag_node.setInput(0, self.previous_node) + self._temp_nodes.append(dag_node) + self.previous_node = dag_node + self.log.debug("OCIODisplay... `{}`".format(self._temp_nodes)) + + # GenerateLUT + gen_lut_node = nuke.createNode("GenerateLUT") + gen_lut_node["file"].setValue(self.path) + gen_lut_node["file_type"].setValue(".{}".format(self.ext)) + gen_lut_node["lut1d"].setValue(self.lut_size) + gen_lut_node["style1d"].setValue(self.lut_style) + # connect + gen_lut_node.setInput(0, self.previous_node) + self._temp_nodes.append(gen_lut_node) + self.log.debug("GenerateLUT... `{}`".format(self._temp_nodes)) + + # ---------- end nodes creation + + # Export lut file + nuke.execute( + gen_lut_node.name(), + int(self.first_frame), + int(self.first_frame)) + + self.log.info("Exported...") + + # ---------- generate representation data + self.get_representation_data() + + self.log.debug("Representation... `{}`".format(self.data)) + + # ---------- Clean up + self.clean_nodes() + + return self.data + + +class ExporterReviewMov(ExporterReview): + """ + Metaclass for generating review mov files + + Args: + klass (pyblish.plugin): pyblish plugin parent + instance (pyblish.instance): instance of pyblish context + + """ + + def __init__(self, + klass, + instance, + name=None, + ext=None, + ): + # initialize parent class + ExporterReview.__init__(self, klass, instance) + + # passing presets for nodes to self + if hasattr(klass, "nodes"): + self.nodes = klass.nodes + else: + self.nodes = {} + + # deal with now lut defined in viewer lut + self.viewer_lut_raw = klass.viewer_lut_raw + self.bake_colorspace_fallback = klass.bake_colorspace_fallback + self.bake_colorspace_main = klass.bake_colorspace_main + self.write_colorspace = instance.data["colorspace"] + + self.name = name or "baked" + self.ext = ext or "mov" + + # set frame start / end and file name to self + self.get_file_info() + + self.log.info("File info was set...") + + self.file = self.fhead + self.name + ".{}".format(self.ext) + self.path = os.path.join( + self.staging_dir, self.file).replace("\\", "/") + + def render(self, render_node_name): + self.log.info("Rendering... ") + # Render Write node + nuke.execute( + render_node_name, + int(self.first_frame), + int(self.last_frame)) + + self.log.info("Rendered...") + + def save_file(self): + import shutil + with anlib.maintained_selection(): + self.log.info("Saving nodes as file... ") + # create nk path + path = os.path.splitext(self.path)[0] + ".nk" + # save file to the path + shutil.copyfile(self.instance.context.data["currentFile"], path) + + self.log.info("Nodes exported...") + return path + + def generate_mov(self, farm=False): + # ---------- start nodes creation + + # Read node + r_node = nuke.createNode("Read") + r_node["file"].setValue(self.path_in) + r_node["first"].setValue(self.first_frame) + r_node["origfirst"].setValue(self.first_frame) + r_node["last"].setValue(self.last_frame) + r_node["origlast"].setValue(self.last_frame) + r_node["colorspace"].setValue(self.write_colorspace) + + # connect + self._temp_nodes.append(r_node) + self.previous_node = r_node + self.log.debug("Read... `{}`".format(self._temp_nodes)) + + # only create colorspace baking if toggled on + if self.bake_colorspace: + # View Process node + ipn = self.get_view_process_node() + if ipn is not None: + # connect + ipn.setInput(0, self.previous_node) + self._temp_nodes.append(ipn) + self.previous_node = ipn + self.log.debug("ViewProcess... `{}`".format(self._temp_nodes)) + + if not self.viewer_lut_raw: + colorspaces = [ + self.bake_colorspace_main, self.bake_colorspace_fallback + ] + + if any(colorspaces): + # OCIOColorSpace with controled output + dag_node = nuke.createNode("OCIOColorSpace") + self._temp_nodes.append(dag_node) + for c in colorspaces: + test = dag_node["out_colorspace"].setValue(str(c)) + if test: + self.log.info( + "Baking in colorspace... `{}`".format(c)) + break + + if not test: + dag_node = nuke.createNode("OCIODisplay") + else: + # OCIODisplay + dag_node = nuke.createNode("OCIODisplay") + + # connect + dag_node.setInput(0, self.previous_node) + self._temp_nodes.append(dag_node) + self.previous_node = dag_node + self.log.debug("OCIODisplay... `{}`".format(self._temp_nodes)) + + # Write node + write_node = nuke.createNode("Write") + self.log.debug("Path: {}".format(self.path)) + write_node["file"].setValue(self.path) + write_node["file_type"].setValue(self.ext) + + # Knobs `meta_codec` and `mov64_codec` are not available on centos. + # TODO change this to use conditions, if possible. + try: + write_node["meta_codec"].setValue("ap4h") + except Exception: + self.log.info("`meta_codec` knob was not found") + + try: + write_node["mov64_codec"].setValue("ap4h") + except Exception: + self.log.info("`mov64_codec` knob was not found") + write_node["mov64_write_timecode"].setValue(1) + write_node["raw"].setValue(1) + # connect + write_node.setInput(0, self.previous_node) + self._temp_nodes.append(write_node) + self.log.debug("Write... `{}`".format(self._temp_nodes)) + # ---------- end nodes creation + + # ---------- render or save to nk + if farm: + nuke.scriptSave() + path_nk = self.save_file() + self.data.update({ + "bakeScriptPath": path_nk, + "bakeWriteNodeName": write_node.name(), + "bakeRenderPath": self.path + }) + else: + self.render(write_node.name()) + # ---------- generate representation data + self.get_representation_data( + tags=["review", "delete"], + range=True + ) + + self.log.debug("Representation... `{}`".format(self.data)) + + # ---------- Clean up + self.clean_nodes() + nuke.scriptSave() + return self.data diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py index a0f1c9a087..d21cb0eef9 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py @@ -1,7 +1,7 @@ import os import pyblish.api from avalon.nuke import lib as anlib -from openpype.hosts.nuke.api import lib as pnlib +from openpype.hosts.nuke.api import plugin import openpype try: @@ -9,7 +9,7 @@ try: except ImportError: from importlib import reload -reload(pnlib) +reload(plugin) class ExtractReviewDataLut(openpype.api.Extractor): @@ -45,7 +45,7 @@ class ExtractReviewDataLut(openpype.api.Extractor): # generate data with anlib.maintained_selection(): - exporter = pnlib.ExporterReviewLut( + exporter = plugin.ExporterReviewLut( self, instance ) data = exporter.generate_lut() diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index f4fbc2d0e4..a3fb00bd0e 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -1,7 +1,7 @@ import os import pyblish.api from avalon.nuke import lib as anlib -from openpype.hosts.nuke.api import lib as pnlib +from openpype.hosts.nuke.api import plugin import openpype try: @@ -9,7 +9,7 @@ try: except ImportError: from importlib import reload -reload(pnlib) +reload(plugin) class ExtractReviewDataMov(openpype.api.Extractor): @@ -47,7 +47,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): # generate data with anlib.maintained_selection(): - exporter = pnlib.ExporterReviewMov( + exporter = plugin.ExporterReviewMov( self, instance) if "render.farm" in families: From 9c21c826a79b97be9654f17e41b663c63ac8d558 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 9 Nov 2021 12:41:08 +0100 Subject: [PATCH 03/27] dealing with baking colorspace in publishing collector --- .../hosts/nuke/plugins/publish/precollect_instances.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_instances.py b/openpype/hosts/nuke/plugins/publish/precollect_instances.py index 5c30df9a62..7bd3b83818 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_instances.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_instances.py @@ -74,6 +74,11 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): if review: families.append("review") + # deside if to bake or not to bake + baking = True + if "bake_colorspace" in node.knobs(): + baking = node["bake_colorspace"].value() + # Add all nodes in group instances. if node.Class() == "Group": # only alter families for render family @@ -142,7 +147,8 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): "resolutionWidth": resolution_width, "resolutionHeight": resolution_height, "pixelAspect": pixel_aspect, - "review": review + "review": review, + "bakeColorspace": baking }) self.log.info("collected instance: {}".format(instance.data)) From c4de365d0650c4f87f61c007ea6f457086bc5862 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 10 Nov 2021 10:57:55 +0100 Subject: [PATCH 04/27] adding baking toggle knobs to render node bake colorspace, bake viewer process --- openpype/hosts/nuke/api/lib.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index e1e417df4d..bea2fe47a9 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -544,8 +544,7 @@ def add_rendering_knobs(node, farm=True): Return: node (obj): with added knobs ''' - knob_options = [ - "Use existing frames", "Local"] + knob_options = ["Use existing frames", "Local"] if farm: knob_options.append("On farm") @@ -568,6 +567,7 @@ def add_review_knob(node): if "review" not in node.knobs(): knob = nuke.Boolean_Knob("review", "Review") knob.setValue(True) + knob.setFlag(nuke.STARTLINE) node.addKnob(knob) return node @@ -585,6 +585,10 @@ def add_bake_colorspace_knob(node): knob = nuke.Boolean_Knob("bake_colorspace", "Bake colorspace") knob.setValue(True) node.addKnob(knob) + if "bake_viewer_input" not in node.knobs(): + knob = nuke.Boolean_Knob("bake_viewer_input", "Bake viewer input") + knob.setValue(True) + node.addKnob(knob) return node From 3a63f587bf32055b6895c8875091588dc3eef9ad Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 10 Nov 2021 10:59:10 +0100 Subject: [PATCH 05/27] improving extractor for multiple processing --- openpype/hosts/nuke/api/plugin.py | 111 ++++++++++++++++-------------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 9801e19126..b753bc0965 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -97,8 +97,7 @@ class NukeLoader(api.Loader): return dependent_nodes - -class ExporterReview: +class ExporterReview(object): """ Base class object for generating review data from Nuke @@ -107,7 +106,6 @@ class ExporterReview: instance (pyblish.instance): instance of pyblish context """ - _temp_nodes = [] data = dict({ "representations": list() }) @@ -120,6 +118,7 @@ class ExporterReview: self.log = klass.log self.instance = instance self.bake_colorspace = instance.data["bakeColorspace"] + self.bake_viewer_input = instance.data["bakeViewerInput"] self.path_in = self.instance.data.get("path", None) self.staging_dir = self.instance.data["stagingDir"] self.collection = self.instance.data.get("collection", None) @@ -198,12 +197,6 @@ class ExporterReview: return ipn - def clean_nodes(self): - for node in self._temp_nodes: - nuke.delete(node) - self._temp_nodes = [] - self.log.info("Deleted nodes...") - class ExporterReviewLut(ExporterReview): """ @@ -215,6 +208,7 @@ class ExporterReviewLut(ExporterReview): """ + _temp_nodes = [] def __init__(self, klass, @@ -225,8 +219,7 @@ class ExporterReviewLut(ExporterReview): lut_size=None, lut_style=None): # initialize parent class - ExporterReview.__init__(self, klass, instance) - self._temp_nodes = [] + super(ExporterReviewLut, self).__init__(klass, instance) # deal with now lut defined in viewer lut if hasattr(klass, "viewer_lut_raw"): @@ -249,6 +242,12 @@ class ExporterReviewLut(ExporterReview): self.path = os.path.join( self.staging_dir, self.file).replace("\\", "/") + def clean_nodes(self): + for node in self._temp_nodes: + nuke.delete(node) + self._temp_nodes = [] + self.log.info("Deleted nodes...") + def generate_lut(self): # ---------- start nodes creation @@ -260,23 +259,26 @@ class ExporterReviewLut(ExporterReview): self.previous_node = cms_node self.log.debug("CMSTestPattern... `{}`".format(self._temp_nodes)) - # Node View Process - ipn = self.get_view_process_node() - if ipn is not None: - # connect - ipn.setInput(0, self.previous_node) - self._temp_nodes.append(ipn) - self.previous_node = ipn - self.log.debug("ViewProcess... `{}`".format(self._temp_nodes)) + if self.bake_colorspace: + # Node View Process + if self.bake_viewer_input: + ipn = self.get_view_process_node() + if ipn is not None: + # connect + ipn.setInput(0, self.previous_node) + self._temp_nodes.append(ipn) + self.previous_node = ipn + self.log.debug( + "ViewProcess... `{}`".format(self._temp_nodes)) - if not self.viewer_lut_raw: - # OCIODisplay - dag_node = nuke.createNode("OCIODisplay") - # connect - dag_node.setInput(0, self.previous_node) - self._temp_nodes.append(dag_node) - self.previous_node = dag_node - self.log.debug("OCIODisplay... `{}`".format(self._temp_nodes)) + if not self.viewer_lut_raw: + # OCIODisplay + dag_node = nuke.createNode("OCIODisplay") + # connect + dag_node.setInput(0, self.previous_node) + self._temp_nodes.append(dag_node) + self.previous_node = dag_node + self.log.debug("OCIODisplay... `{}`".format(self._temp_nodes)) # GenerateLUT gen_lut_node = nuke.createNode("GenerateLUT") @@ -319,6 +321,7 @@ class ExporterReviewMov(ExporterReview): instance (pyblish.instance): instance of pyblish context """ + _temp_nodes = {} def __init__(self, klass, @@ -327,13 +330,9 @@ class ExporterReviewMov(ExporterReview): ext=None, ): # initialize parent class - ExporterReview.__init__(self, klass, instance) - + super(ExporterReviewMov, self).__init__(klass, instance) # passing presets for nodes to self - if hasattr(klass, "nodes"): - self.nodes = klass.nodes - else: - self.nodes = {} + self.nodes = klass.nodes if hasattr(klass, "nodes") else {} # deal with now lut defined in viewer lut self.viewer_lut_raw = klass.viewer_lut_raw @@ -353,6 +352,12 @@ class ExporterReviewMov(ExporterReview): self.path = os.path.join( self.staging_dir, self.file).replace("\\", "/") + def clean_nodes(self, node_name): + for node in self._temp_nodes[node_name]: + nuke.delete(node) + self._temp_nodes[node_name] = [] + self.log.info("Deleted nodes...") + def render(self, render_node_name): self.log.info("Rendering... ") # Render Write node @@ -376,6 +381,8 @@ class ExporterReviewMov(ExporterReview): return path def generate_mov(self, farm=False): + subset = self.instance.data["subset"] + self._temp_nodes[subset] = [] # ---------- start nodes creation # Read node @@ -388,20 +395,23 @@ class ExporterReviewMov(ExporterReview): r_node["colorspace"].setValue(self.write_colorspace) # connect - self._temp_nodes.append(r_node) + self._temp_nodes[subset].append(r_node) self.previous_node = r_node - self.log.debug("Read... `{}`".format(self._temp_nodes)) + self.log.debug("Read... `{}`".format(self._temp_nodes[subset])) # only create colorspace baking if toggled on if self.bake_colorspace: - # View Process node - ipn = self.get_view_process_node() - if ipn is not None: - # connect - ipn.setInput(0, self.previous_node) - self._temp_nodes.append(ipn) - self.previous_node = ipn - self.log.debug("ViewProcess... `{}`".format(self._temp_nodes)) + if self.bake_viewer_input: + # View Process node + ipn = self.get_view_process_node() + if ipn is not None: + # connect + ipn.setInput(0, self.previous_node) + self._temp_nodes[subset].append(ipn) + self.previous_node = ipn + self.log.debug( + "ViewProcess... `{}`".format( + self._temp_nodes[subset])) if not self.viewer_lut_raw: colorspaces = [ @@ -411,7 +421,7 @@ class ExporterReviewMov(ExporterReview): if any(colorspaces): # OCIOColorSpace with controled output dag_node = nuke.createNode("OCIOColorSpace") - self._temp_nodes.append(dag_node) + self._temp_nodes[subset].append(dag_node) for c in colorspaces: test = dag_node["out_colorspace"].setValue(str(c)) if test: @@ -427,9 +437,10 @@ class ExporterReviewMov(ExporterReview): # connect dag_node.setInput(0, self.previous_node) - self._temp_nodes.append(dag_node) + self._temp_nodes[subset].append(dag_node) self.previous_node = dag_node - self.log.debug("OCIODisplay... `{}`".format(self._temp_nodes)) + self.log.debug("OCIODisplay... `{}`".format( + self._temp_nodes[subset])) # Write node write_node = nuke.createNode("Write") @@ -452,8 +463,8 @@ class ExporterReviewMov(ExporterReview): write_node["raw"].setValue(1) # connect write_node.setInput(0, self.previous_node) - self._temp_nodes.append(write_node) - self.log.debug("Write... `{}`".format(self._temp_nodes)) + self._temp_nodes[subset].append(write_node) + self.log.debug("Write... `{}`".format(self._temp_nodes[subset])) # ---------- end nodes creation # ---------- render or save to nk @@ -475,7 +486,7 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Representation... `{}`".format(self.data)) - # ---------- Clean up - self.clean_nodes() + self.clean_nodes(subset) nuke.scriptSave() + return self.data From 83b62aad14d34ffa776c40b4c5b985cb73cc1786 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 10 Nov 2021 11:00:17 +0100 Subject: [PATCH 06/27] better pep8 --- openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index a3fb00bd0e..83275f9716 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -35,7 +35,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): self.log.info("Creating staging dir...") if "representations" not in instance.data: - instance.data["representations"] = list() + instance.data["representations"] = [] staging_dir = os.path.normpath( os.path.dirname(instance.data['path'])) From ab68b5ee4fdfe3df4ca01cbad6d6cedf95705eb7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 10 Nov 2021 11:01:00 +0100 Subject: [PATCH 07/27] baking toggles to precollect instances --- .../nuke/plugins/publish/precollect_instances.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_instances.py b/openpype/hosts/nuke/plugins/publish/precollect_instances.py index 7bd3b83818..c34e314a79 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_instances.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_instances.py @@ -79,6 +79,16 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): if "bake_colorspace" in node.knobs(): baking = node["bake_colorspace"].value() + if baking: + families.append("bake_viewer") + + viewer_input = True + if "bake_viewer_input" in node.knobs(): + viewer_input = node["bake_viewer_input"].value() + + if viewer_input: + families.append("bake_viewer_input") + # Add all nodes in group instances. if node.Class() == "Group": # only alter families for render family @@ -148,7 +158,8 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): "resolutionHeight": resolution_height, "pixelAspect": pixel_aspect, "review": review, - "bakeColorspace": baking + "bakeColorspace": baking, + "bakeViewerInput": viewer_input }) self.log.info("collected instance: {}".format(instance.data)) From 3c88483475fbe368001927b1595675a470d90896 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 10 Nov 2021 14:45:17 +0100 Subject: [PATCH 08/27] enhancing settings for baking multiple profiles with defined imageio profile --- .../defaults/project_anatomy/imageio.json | 3 + .../defaults/project_settings/nuke.json | 17 +++++- .../schemas/schema_anatomy_imageio.json | 15 ++++- .../schemas/schema_nuke_publish.json | 55 +++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/imageio.json b/openpype/settings/defaults/project_anatomy/imageio.json index fc34ef6813..09ab398c37 100644 --- a/openpype/settings/defaults/project_anatomy/imageio.json +++ b/openpype/settings/defaults/project_anatomy/imageio.json @@ -28,6 +28,9 @@ "viewer": { "viewerProcess": "sRGB" }, + "baking": { + "viewerProcess": "rec709" + }, "workfile": { "colorManagement": "Nuke", "OCIO_config": "nuke-default", diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index e3c7834e4a..67446ca6b9 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -103,7 +103,19 @@ }, "ExtractReviewDataMov": { "enabled": true, - "viewer_lut_raw": false + "viewer_lut_raw": false, + "outputs": { + "baking": { + "filter": { + "task_types": [], + "families": [] + }, + "viewer_process_override": "", + "bake_viewer_process": true, + "bake_viewer_input_process": true, + "add_taggs": [] + } + } }, "ExtractSlateFrame": { "viewer_lut_raw": false @@ -130,8 +142,7 @@ }, "LoadClip": { "enabled": true, - "_representations": [ - ], + "_representations": [], "node_name_template": "{class_name}_{ext}" } }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json index 7423d6fd3e..380ea4a83d 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json @@ -131,6 +131,19 @@ } ] }, + { + "key": "baking", + "type": "dict", + "label": "Extract-review baking profile", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "viewerProcess", + "label": "Viewer Process" + } + ] + }, { "key": "workfile", "type": "dict", @@ -363,7 +376,7 @@ "key": "maya", "type": "dict", "label": "Maya", - "children": [ + "children": [ { "key": "colorManagementPreference", "type": "dict", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 74b2592d29..1bdd15c650 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -167,7 +167,62 @@ "type": "boolean", "key": "viewer_lut_raw", "label": "Viewer LUT raw" + }, + { + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "type": "dict", + "collapsible": false, + "key": "filter", + "label": "Filtering", + "children": [ + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "separator" + }, + { + "type": "text", + "key": "viewer_process_override", + "label": "Viewer Process colorspace profile override" + }, + { + "type": "boolean", + "key": "bake_viewer_process", + "label": "Bake Viewer Process" + }, + { + "type": "boolean", + "key": "bake_viewer_input_process", + "label": "Bake Viewer Input Process (LUTs)" + }, + { + "key": "add_taggs", + "label": "Add tags to representations", + "type": "list", + "object_type": "text" + } + ] + } } + ] }, { From 3d9244a39929cafd235acdac72cd5d3b53bb2b47 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Nov 2021 11:21:41 +0100 Subject: [PATCH 09/27] nuke: reversing baking toggles from write node --- openpype/hosts/nuke/api/lib.py | 35 +++++++--------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index bea2fe47a9..2c91ebc5ae 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -37,6 +37,10 @@ opnl.workfiles_launched = False opnl._node_tab_name = "{}".format(os.getenv("AVALON_LABEL") or "Avalon") +def get_nuke_imageio_settings(): + return get_anatomy_settings(opnl.project_name)["imageio"]["nuke"] + + def get_created_node_imageio_setting(**kwarg): ''' Get preset data for dataflow (fileType, compression, bitDepth) ''' @@ -47,8 +51,7 @@ def get_created_node_imageio_setting(**kwarg): assert any([creator, nodeclass]), nuke.message( "`{}`: Missing mandatory kwargs `host`, `cls`".format(__file__)) - imageio = get_anatomy_settings(opnl.project_name)["imageio"] - imageio_nodes = imageio["nuke"]["nodes"]["requiredNodes"] + imageio_nodes = get_nuke_imageio_settings()["nodes"]["requiredNodes"] imageio_node = None for node in imageio_nodes: @@ -66,8 +69,7 @@ def get_imageio_input_colorspace(filename): ''' Get input file colorspace based on regex in settings. ''' imageio_regex_inputs = ( - get_anatomy_settings(opnl.project_name) - ["imageio"]["nuke"]["regexInputs"]["inputs"]) + get_nuke_imageio_settings()["regexInputs"]["inputs"]) preset_clrsp = None for regexInput in imageio_regex_inputs: @@ -478,7 +480,6 @@ def create_write_node(name, data, input=None, prenodes=None, if review: add_review_knob(GN) - add_bake_colorspace_knob(GN) # add divider GN.addKnob(nuke.Text_Knob('', 'Rendering')) @@ -567,27 +568,6 @@ def add_review_knob(node): if "review" not in node.knobs(): knob = nuke.Boolean_Knob("review", "Review") knob.setValue(True) - knob.setFlag(nuke.STARTLINE) - node.addKnob(knob) - return node - - -def add_bake_colorspace_knob(node): - ''' Adds additional bake colorspace knob to given node - - Arguments: - node (obj): nuke node object to be fixed - - Return: - node (obj): with added knob - ''' - if "bake_colorspace" not in node.knobs(): - knob = nuke.Boolean_Knob("bake_colorspace", "Bake colorspace") - knob.setValue(True) - node.addKnob(knob) - if "bake_viewer_input" not in node.knobs(): - knob = nuke.Boolean_Knob("bake_viewer_input", "Bake viewer input") - knob.setValue(True) node.addKnob(knob) return node @@ -923,8 +903,7 @@ class WorkfileSettings(object): ''' Setting colorpace following presets ''' # get imageio - imageio = get_anatomy_settings(opnl.project_name)["imageio"] - nuke_colorspace = imageio["nuke"] + nuke_colorspace = get_nuke_imageio_settings() try: self.set_root_colorspace(nuke_colorspace["workfile"]) From 9f86e8d3e322bb3fc3f531271f01301feca89b0e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Nov 2021 11:23:20 +0100 Subject: [PATCH 10/27] Nuke: adding settings to plugin orchestrating baking workflow also removing rest of the on write node toggles --- openpype/hosts/nuke/api/plugin.py | 86 +++++++++--------- .../publish/extract_review_data_mov.py | 87 ++++++++++++++----- .../plugins/publish/precollect_instances.py | 19 +--- .../defaults/project_settings/nuke.json | 4 +- .../schemas/schema_nuke_publish.json | 15 +++- 5 files changed, 129 insertions(+), 82 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index b753bc0965..7b8af96df0 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -117,8 +117,6 @@ class ExporterReview(object): self.log = klass.log self.instance = instance - self.bake_colorspace = instance.data["bakeColorspace"] - self.bake_viewer_input = instance.data["bakeViewerInput"] self.path_in = self.instance.data.get("path", None) self.staging_dir = self.instance.data["stagingDir"] self.collection = self.instance.data.get("collection", None) @@ -146,11 +144,10 @@ class ExporterReview(object): self.fhead = self.fhead.replace("#", "")[:-1] def get_representation_data(self, tags=None, range=False): - add_tags = [] - if tags: - add_tags = tags + add_tags = tags or [] repre = { + 'outputName': self.name, 'name': self.name, 'ext': self.ext, 'files': self.file, @@ -166,7 +163,7 @@ class ExporterReview(object): self.data["representations"].append(repre) - def get_view_process_node(self): + def get_view_input_process_node(self): """ Will get any active view process. @@ -197,6 +194,11 @@ class ExporterReview(object): return ipn + def get_imageio_baking_profile(self): + from . import lib as opnlib + nuke_imageio = opnlib.get_nuke_imageio_settings() + return nuke_imageio["baking"]["viewerProcess"] + class ExporterReviewLut(ExporterReview): """ @@ -249,6 +251,10 @@ class ExporterReviewLut(ExporterReview): self.log.info("Deleted nodes...") def generate_lut(self): + bake_viewer_process = kwargs["bake_viewer_process"] + bake_viewer_input_process_node = kwargs[ + "bake_viewer_input_process"] + # ---------- start nodes creation # CMSTestPattern @@ -259,10 +265,10 @@ class ExporterReviewLut(ExporterReview): self.previous_node = cms_node self.log.debug("CMSTestPattern... `{}`".format(self._temp_nodes)) - if self.bake_colorspace: + if bake_viewer_process: # Node View Process - if self.bake_viewer_input: - ipn = self.get_view_process_node() + if bake_viewer_input_process_node: + ipn = self.get_view_input_process_node() if ipn is not None: # connect ipn.setInput(0, self.previous_node) @@ -336,8 +342,6 @@ class ExporterReviewMov(ExporterReview): # deal with now lut defined in viewer lut self.viewer_lut_raw = klass.viewer_lut_raw - self.bake_colorspace_fallback = klass.bake_colorspace_fallback - self.bake_colorspace_main = klass.bake_colorspace_main self.write_colorspace = instance.data["colorspace"] self.name = name or "baked" @@ -380,7 +384,26 @@ class ExporterReviewMov(ExporterReview): self.log.info("Nodes exported...") return path - def generate_mov(self, farm=False): + def generate_mov(self, farm=False, **kwargs): + bake_viewer_process = kwargs["bake_viewer_process"] + bake_viewer_input_process_node = kwargs[ + "bake_viewer_input_process"] + viewer_process_override = kwargs[ + "viewer_process_override"] + + baking_view_profile = ( + viewer_process_override or self.get_imageio_baking_profile()) + + fps = self.instance.context.data["fps"] + + self.log.debug(">> baking_view_profile `{}`".format( + baking_view_profile)) + + add_tags = kwargs.get("add_tags", []) + + self.log.info( + "__ add_tags: `{0}`".format(add_tags)) + subset = self.instance.data["subset"] self._temp_nodes[subset] = [] # ---------- start nodes creation @@ -400,10 +423,10 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Read... `{}`".format(self._temp_nodes[subset])) # only create colorspace baking if toggled on - if self.bake_colorspace: - if self.bake_viewer_input: + if bake_viewer_process: + if bake_viewer_input_process_node: # View Process node - ipn = self.get_view_process_node() + ipn = self.get_view_input_process_node() if ipn is not None: # connect ipn.setInput(0, self.previous_node) @@ -414,26 +437,9 @@ class ExporterReviewMov(ExporterReview): self._temp_nodes[subset])) if not self.viewer_lut_raw: - colorspaces = [ - self.bake_colorspace_main, self.bake_colorspace_fallback - ] - - if any(colorspaces): - # OCIOColorSpace with controled output - dag_node = nuke.createNode("OCIOColorSpace") - self._temp_nodes[subset].append(dag_node) - for c in colorspaces: - test = dag_node["out_colorspace"].setValue(str(c)) - if test: - self.log.info( - "Baking in colorspace... `{}`".format(c)) - break - - if not test: - dag_node = nuke.createNode("OCIODisplay") - else: - # OCIODisplay - dag_node = nuke.createNode("OCIODisplay") + # OCIODisplay + dag_node = nuke.createNode("OCIODisplay") + dag_node["view"].setValue(str(baking_view_profile)) # connect dag_node.setInput(0, self.previous_node) @@ -445,11 +451,11 @@ class ExporterReviewMov(ExporterReview): # Write node write_node = nuke.createNode("Write") self.log.debug("Path: {}".format(self.path)) - write_node["file"].setValue(self.path) - write_node["file_type"].setValue(self.ext) + write_node["file"].setValue(str(self.path)) + write_node["file_type"].setValue(str(self.ext)) # Knobs `meta_codec` and `mov64_codec` are not available on centos. - # TODO change this to use conditions, if possible. + # TODO should't this come from settings on outputs? try: write_node["meta_codec"].setValue("ap4h") except Exception: @@ -457,8 +463,10 @@ class ExporterReviewMov(ExporterReview): try: write_node["mov64_codec"].setValue("ap4h") + write_node["mov64_fps"].setValue(float(fps)) except Exception: self.log.info("`mov64_codec` knob was not found") + write_node["mov64_write_timecode"].setValue(1) write_node["raw"].setValue(1) # connect @@ -480,7 +488,7 @@ class ExporterReviewMov(ExporterReview): self.render(write_node.name()) # ---------- generate representation data self.get_representation_data( - tags=["review", "delete"], + tags=["review", "delete"] + add_tags, range=True ) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 83275f9716..f092700e3b 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -27,11 +27,11 @@ class ExtractReviewDataMov(openpype.api.Extractor): # presets viewer_lut_raw = None - bake_colorspace_fallback = None - bake_colorspace_main = None + outputs = {} def process(self, instance): families = instance.data["families"] + task_type = instance.context.data["taskType"] self.log.info("Creating staging dir...") if "representations" not in instance.data: @@ -45,28 +45,71 @@ class ExtractReviewDataMov(openpype.api.Extractor): self.log.info( "StagingDir `{0}`...".format(instance.data["stagingDir"])) + self.log.info(self.outputs) + # generate data with anlib.maintained_selection(): - exporter = plugin.ExporterReviewMov( - self, instance) + for o_name, o_data in self.outputs.items(): + f_families = o_data["filter"]["families"] + f_task_types = o_data["filter"]["task_types"] - if "render.farm" in families: - instance.data["families"].remove("review") - data = exporter.generate_mov(farm=True) + test_families = any([ + bool(set(families).intersection(f_families)), + bool(not f_families) + ]) + + test_task_types = any([ + bool(task_type in f_task_types), + bool(not f_task_types) + ]) + + test_all = all([ + test_families, + test_task_types + ]) + + if not test_all: + continue + + self.log.info( + "Baking output `{}` with settings: {}".format( + o_name, o_data)) + + # add additional families + add_families = o_data["add_families"] + for adf in add_families: + if adf in instance.data["families"]: + continue + instance.data["families"].append(adf) + + # create exporter instance + exporter = plugin.ExporterReviewMov( + self, instance, o_name, o_data["extension"]) + + if "render.farm" in families: + if "review" in instance.data["families"]: + instance.data["families"].remove("review") + + data = exporter.generate_mov(farm=True, **o_data) + + self.log.debug( + "_ data: {}".format(data)) + + if not instance.data.get("bakingNukeScripts"): + instance.data["bakingNukeScripts"] = [] + + instance.data["bakingNukeScripts"].append({ + "bakeRenderPath": data.get("bakeRenderPath"), + "bakeScriptPath": data.get("bakeScriptPath"), + "bakeWriteNodeName": data.get("bakeWriteNodeName") + }) + else: + data = exporter.generate_mov(**o_data) + + self.log.info(data["representations"]) + + # assign to representations + instance.data["representations"] += data["representations"] self.log.debug( - "_ data: {}".format(data)) - - instance.data.update({ - "bakeRenderPath": data.get("bakeRenderPath"), - "bakeScriptPath": data.get("bakeScriptPath"), - "bakeWriteNodeName": data.get("bakeWriteNodeName") - }) - else: - data = exporter.generate_mov() - - # assign to representations - instance.data["representations"] += data["representations"] - - self.log.debug( - "_ representations: {}".format(instance.data["representations"])) + "_ representations: {}".format(instance.data["representations"])) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_instances.py b/openpype/hosts/nuke/plugins/publish/precollect_instances.py index c34e314a79..5c30df9a62 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_instances.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_instances.py @@ -74,21 +74,6 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): if review: families.append("review") - # deside if to bake or not to bake - baking = True - if "bake_colorspace" in node.knobs(): - baking = node["bake_colorspace"].value() - - if baking: - families.append("bake_viewer") - - viewer_input = True - if "bake_viewer_input" in node.knobs(): - viewer_input = node["bake_viewer_input"].value() - - if viewer_input: - families.append("bake_viewer_input") - # Add all nodes in group instances. if node.Class() == "Group": # only alter families for render family @@ -157,9 +142,7 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): "resolutionWidth": resolution_width, "resolutionHeight": resolution_height, "pixelAspect": pixel_aspect, - "review": review, - "bakeColorspace": baking, - "bakeViewerInput": viewer_input + "review": review }) self.log.info("collected instance: {}".format(instance.data)) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 67446ca6b9..ed0db6a000 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -110,10 +110,12 @@ "task_types": [], "families": [] }, + "extension": "mov", "viewer_process_override": "", "bake_viewer_process": true, "bake_viewer_input_process": true, - "add_taggs": [] + "add_tags": [], + "add_families": [] } } }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 1bdd15c650..b1f640e951 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -198,6 +198,11 @@ { "type": "separator" }, + { + "type": "text", + "key": "extension", + "label": "File extension" + }, { "type": "text", "key": "viewer_process_override", @@ -214,8 +219,14 @@ "label": "Bake Viewer Input Process (LUTs)" }, { - "key": "add_taggs", - "label": "Add tags to representations", + "key": "add_families", + "label": "Add additional families to instance", + "type": "list", + "object_type": "text" + }, + { + "key": "add_tags", + "label": "Add additional tags to representations", "type": "list", "object_type": "text" } From 162d3864a11a541f5516e2a335bd64cb2f34c05d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Nov 2021 11:23:56 +0100 Subject: [PATCH 11/27] nuke: adding parent to workfile tool (it is not working yet) --- openpype/hosts/nuke/api/menu.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/menu.py b/openpype/hosts/nuke/api/menu.py index 3e74893589..d92a99a965 100644 --- a/openpype/hosts/nuke/api/menu.py +++ b/openpype/hosts/nuke/api/menu.py @@ -1,6 +1,7 @@ import os import nuke from avalon.api import Session +from avalon.nuke.pipeline import get_main_window from .lib import WorkfileSettings from openpype.api import Logger, BuildWorkfile, get_current_project_settings @@ -25,7 +26,7 @@ def install(): menu.removeItem(rm_item[1].name()) menu.addCommand( name, - host_tools.show_workfiles, + lambda: host_tools.show_workfiles(parent=get_main_window()), index=2 ) menu.addSeparator(index=3) From 49890910504906f30d885806f0e2d157a3495bcf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Nov 2021 11:24:44 +0100 Subject: [PATCH 12/27] Global: adding burnin profiles linking to extract review profile --- openpype/settings/defaults/project_settings/global.json | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 45c1a59d17..0eacfdd768 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -49,6 +49,7 @@ "burnin", "ftrackreview" ], + "burnins": [], "ffmpeg_args": { "video_filters": [], "audio_filters": [], From 28e28db6c76aec511ae3c0da398666e3681386d1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Nov 2021 11:25:26 +0100 Subject: [PATCH 13/27] Global: implementing burnin profiles linking to extract review --- openpype/plugins/publish/extract_burnin.py | 47 +++++++++++++++---- openpype/plugins/publish/extract_review.py | 19 +++++++- .../schemas/schema_global_publish.json | 6 +++ 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py index 06eb85c593..8015e5b54b 100644 --- a/openpype/plugins/publish/extract_burnin.py +++ b/openpype/plugins/publish/extract_burnin.py @@ -110,6 +110,9 @@ class ExtractBurnin(openpype.api.Extractor): ).format(host_name, family, task_name)) return + self.log.debug("profile: {}".format( + profile)) + # Pre-filter burnin definitions by instance families burnin_defs = self.filter_burnins_defs(profile, instance) if not burnin_defs: @@ -126,18 +129,44 @@ class ExtractBurnin(openpype.api.Extractor): anatomy = instance.context.data["anatomy"] scriptpath = self.burnin_script_path() + # Executable args that will execute the script # [pype executable, *pype script, "run"] executable_args = get_pype_execute_args("run", scriptpath) + from pprint import pformat + self.log.debug(pformat(instance.data["representations"])) + for idx, repre in enumerate(tuple(instance.data["representations"])): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + + repre_burnin_links = repre.get("burnins", []) + if not self.repres_is_valid(repre): continue + self.log.debug("repre_burnin_links: {}".format( + repre_burnin_links)) + + self.log.debug("burnin_defs.keys(): {}".format( + burnin_defs.keys())) + + # Filter output definition by `burnin` represetation key + repre_linked_burnins = { + name: output for name, output in burnin_defs.items() + if name in repre_burnin_links + } + self.log.debug("repre_linked_burnins: {}".format( + repre_linked_burnins)) + + # if any match then replace burnin defs and follow tag filtering + _burnin_defs = copy.deepcopy(burnin_defs) + if repre_linked_burnins: + _burnin_defs = repre_linked_burnins + # Filter output definition by representation tags (optional) repre_burnin_defs = self.filter_burnins_by_tags( - burnin_defs, repre["tags"] + _burnin_defs, repre["tags"] ) if not repre_burnin_defs: self.log.info(( @@ -281,14 +310,16 @@ class ExtractBurnin(openpype.api.Extractor): # NOTE we maybe can keep source representation if necessary instance.data["representations"].remove(repre) - # Delete input files - for filepath in files_to_delete: - if os.path.exists(filepath): - os.remove(filepath) - self.log.debug("Removed: \"{}\"".format(filepath)) + self.log.debug("Files to delete: {}".format(files_to_delete)) - if do_decompress and os.path.exists(decompressed_dir): - shutil.rmtree(decompressed_dir) + # Delete input files + for filepath in files_to_delete: + if os.path.exists(filepath): + os.remove(filepath) + self.log.debug("Removed: \"{}\"".format(filepath)) + + if do_decompress and os.path.exists(decompressed_dir): + shutil.rmtree(decompressed_dir) def _get_burnin_options(self): # Prepare burnin options diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 7284483f5f..81225cec62 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -180,6 +180,9 @@ class ExtractReview(pyblish.api.InstancePlugin): if "tags" not in output_def: output_def["tags"] = [] + if "burnins" not in output_def: + output_def["burnins"] = [] + # Create copy of representation new_repre = copy.deepcopy(repre) @@ -192,6 +195,17 @@ class ExtractReview(pyblish.api.InstancePlugin): if tag not in new_repre["tags"]: new_repre["tags"].append(tag) + # Add burnin link from output definition to representation + for burnin in output_def["burnins"]: + if burnin not in new_repre.get("burnins", []): + if not new_repre.get("burnins"): + new_repre["burnins"] = [] + new_repre["burnins"].append(str(burnin)) + + self.log.debug( + "Linked burnins: `{}`".format(new_repre["burnins"]) + ) + self.log.debug( "New representation tags: `{}`".format(new_repre["tags"]) ) @@ -232,7 +246,10 @@ class ExtractReview(pyblish.api.InstancePlugin): for f in files_to_clean: os.unlink(f) - output_name = output_def["filename_suffix"] + output_name = new_repre.get("outputName", "") + if output_name: + output_name += "_" + output_name += output_def["filename_suffix"] if temp_data["without_handles"]: output_name += "_noHandles" diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c50f383f02..db83ba1192 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -198,6 +198,12 @@ "type": "schema", "name": "schema_representation_tags" }, + { + "key": "burnins", + "label": "Link to a burnin by name", + "type": "list", + "object_type": "text" + }, { "key": "ffmpeg_args", "label": "FFmpeg arguments", From 4eb71bc5764fa6fb6cf0ec8a4d56b2f08a5ac63d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Nov 2021 11:25:54 +0100 Subject: [PATCH 14/27] Nuke/Global: new baking workflow to Deadline plugins --- .../plugins/publish/submit_nuke_deadline.py | 35 ++++++++++--------- .../plugins/publish/submit_publish_job.py | 12 +++---- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/openpype/modules/default_modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/default_modules/deadline/plugins/publish/submit_nuke_deadline.py index 4cba35963c..a064a0aa86 100644 --- a/openpype/modules/default_modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/default_modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -94,24 +94,27 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): render_path).replace("\\", "/") instance.data["publishJobState"] = "Suspended" - if instance.data.get("bakeScriptPath"): - render_path = instance.data.get("bakeRenderPath") - script_path = instance.data.get("bakeScriptPath") - exe_node_name = instance.data.get("bakeWriteNodeName") + if instance.data.get("bakingNukeScripts"): + for baking_script in instance.data["bakingNukeScripts"]: + render_path = baking_script["bakeRenderPath"] + script_path = baking_script["bakeScriptPath"] + exe_node_name = baking_script["bakeWriteNodeName"] - # exception for slate workflow - if "slate" in instance.data["families"]: - self._frame_start += 1 + # exception for slate workflow + if "slate" in instance.data["families"]: + self._frame_start += 1 - resp = self.payload_submit(instance, - script_path, - render_path, - exe_node_name, - response.json() - ) - # Store output dir for unified publisher (filesequence) - instance.data["deadlineSubmissionJob"] = resp.json() - instance.data["publishJobState"] = "Suspended" + resp = self.payload_submit( + instance, + script_path, + render_path, + exe_node_name, + response.json() + ) + + # Store output dir for unified publisher (filesequence) + instance.data["deadlineSubmissionJob"] = resp.json() + instance.data["publishJobState"] = "Suspended" # redefinition of families if "render.farm" in families: diff --git a/openpype/modules/default_modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/default_modules/deadline/plugins/publish/submit_publish_job.py index 6b07749819..7c7e1f4025 100644 --- a/openpype/modules/default_modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/default_modules/deadline/plugins/publish/submit_publish_job.py @@ -142,8 +142,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): instance_transfer = { "slate": ["slateFrame"], "review": ["lutPath"], - "render2d": ["bakeScriptPath", "bakeRenderPath", - "bakeWriteNodeName", "version"], + "render2d": ["bakingNukeScripts", "version"], "renderlayer": ["convertToScanline"] } @@ -505,9 +504,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): """ representations = [] collections, remainders = clique.assemble(exp_files) - bake_render_path = instance.get("bakeRenderPath", []) + bake_renders = instance.get("bakingNukeScripts", []) - # create representation for every collected sequence + # create representation for every collected sequento ce for collection in collections: ext = collection.tail.lstrip(".") preview = False @@ -523,7 +522,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): preview = True break - if bake_render_path: + if bake_renders: preview = False staging = os.path.dirname(list(collection)[0]) @@ -595,7 +594,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): }) self._solve_families(instance, True) - if remainder in bake_render_path: + if (bake_renders + and remainder in bake_renders[0]["bakeRenderPath"]): rep.update({ "fps": instance.get("fps"), "tags": ["review", "delete"] From bf308ebecd36113f0ccc4c59ff1889c58aaa6a2a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 15 Nov 2021 13:12:12 +0100 Subject: [PATCH 15/27] improving the output name with ext and upstream outputName value --- openpype/plugins/publish/extract_review.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index da1d493067..e500bb361d 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -247,6 +247,7 @@ class ExtractReview(pyblish.api.InstancePlugin): os.unlink(f) output_name = new_repre.get("outputName", "") + output_ext = new_repre["ext"] if output_name: output_name += "_" output_name += output_def["filename_suffix"] @@ -254,7 +255,7 @@ class ExtractReview(pyblish.api.InstancePlugin): output_name += "_noHandles" new_repre.update({ - "name": output_def["filename_suffix"], + "name": "{}_{}".format(output_name, output_ext), "outputName": output_name, "outputDef": output_def, "frameStartFtrack": temp_data["output_frame_start"], From 2f9ce16f174f286b7c8eaee9e9c5deb3b9fedf7f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 15 Nov 2021 16:17:42 +0100 Subject: [PATCH 16/27] otio burnin script supporting mxf source timecode --- openpype/scripts/otio_burnin.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/openpype/scripts/otio_burnin.py b/openpype/scripts/otio_burnin.py index 206abfc0b4..341604763f 100644 --- a/openpype/scripts/otio_burnin.py +++ b/openpype/scripts/otio_burnin.py @@ -37,7 +37,7 @@ TIMECODE_KEY = "{timecode}" SOURCE_TIMECODE_KEY = "{source_timecode}" -def _streams(source): +def _ffprobe_data(source): """Reimplemented from otio burnins to be able use full path to ffprobe :param str source: source media file :rtype: [{}, ...] @@ -47,7 +47,7 @@ def _streams(source): out = proc.communicate()[0] if proc.returncode != 0: raise RuntimeError("Failed to run: %s" % command) - return json.loads(out)['streams'] + return json.loads(out) def get_fps(str_value): @@ -244,15 +244,16 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): } def __init__( - self, source, streams=None, options_init=None, first_frame=None + self, source, ffprobe_data=None, options_init=None, first_frame=None ): - if not streams: - streams = _streams(source) + if not ffprobe_data: + ffprobe_data = _ffprobe_data(source) + self.ffprobe_data = ffprobe_data self.first_frame = first_frame self.input_args = [] - super().__init__(source, streams) + super().__init__(source, ffprobe_data["streams"]) if options_init: self.options_init.update(options_init) @@ -564,11 +565,11 @@ def burnins_from_data( "shot": "sh0010" } """ - streams = None + ffprobe_data = None if full_input_path: - streams = _streams(full_input_path) + ffprobe_data = _ffprobe_data(full_input_path) - burnin = ModifiedBurnins(input_path, streams, options, first_frame) + burnin = ModifiedBurnins(input_path, ffprobe_data, options, first_frame) frame_start = data.get("frame_start") frame_end = data.get("frame_end") @@ -595,6 +596,12 @@ def burnins_from_data( if source_timecode is None: source_timecode = stream.get("tags", {}).get("timecode") + if source_timecode is None: + input_format = burnin.ffprobe_data.get("format") or {} + source_timecode = input_format.get("timecode") + if source_timecode is None: + source_timecode = input_format.get("tags", {}).get("timecode") + if source_timecode is not None: data[SOURCE_TIMECODE_KEY[1:-1]] = SOURCE_TIMECODE_KEY From c3274cc555f1845738b99b03319254d2bafd1afb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 16 Nov 2021 10:49:03 +0100 Subject: [PATCH 17/27] hound suggestions --- openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py | 3 ++- openpype/plugins/publish/extract_burnin.py | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index f092700e3b..cd918afe39 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -112,4 +112,5 @@ class ExtractReviewDataMov(openpype.api.Extractor): instance.data["representations"] += data["representations"] self.log.debug( - "_ representations: {}".format(instance.data["representations"])) + "_ representations: {}".format( + instance.data["representations"])) diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py index 8015e5b54b..b1ba4ddeac 100644 --- a/openpype/plugins/publish/extract_burnin.py +++ b/openpype/plugins/publish/extract_burnin.py @@ -134,9 +134,6 @@ class ExtractBurnin(openpype.api.Extractor): # [pype executable, *pype script, "run"] executable_args = get_pype_execute_args("run", scriptpath) - from pprint import pformat - self.log.debug(pformat(instance.data["representations"])) - for idx, repre in enumerate(tuple(instance.data["representations"])): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) From 8839abd0f97c6a680d532dd80c4d21ad7127b8f6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 16 Nov 2021 10:49:38 +0100 Subject: [PATCH 18/27] Nuke: settings removing additional family --- openpype/settings/defaults/project_settings/nuke.json | 3 +-- .../projects_schema/schemas/schema_nuke_publish.json | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 9b7a0c3090..c3e229b8e8 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -121,8 +121,7 @@ "viewer_process_override": "", "bake_viewer_process": true, "bake_viewer_input_process": true, - "add_tags": [], - "add_families": [] + "add_tags": [] } } }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index b1f640e951..d6fc30c315 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -218,12 +218,6 @@ "key": "bake_viewer_input_process", "label": "Bake Viewer Input Process (LUTs)" }, - { - "key": "add_families", - "label": "Add additional families to instance", - "type": "list", - "object_type": "text" - }, { "key": "add_tags", "label": "Add additional tags to representations", From 172a143ecbdf39b18c36148f086d6b641d59cf9b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 16 Nov 2021 11:56:20 +0100 Subject: [PATCH 19/27] plugin_tools: adding exception because 'mxf` is not supported by oiio --- openpype/lib/plugin_tools.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index aa9e0c9b57..12c77abead 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -531,12 +531,20 @@ def should_decompress(file_url): and we can decompress (oiiotool supported) """ if oiio_supported(): - output = run_subprocess([ - get_oiio_tools_path(), - "--info", "-v", file_url]) - return "compression: \"dwaa\"" in output or \ - "compression: \"dwab\"" in output - + try: + output = run_subprocess([ + get_oiio_tools_path(), + "--info", "-v", file_url]) + return "compression: \"dwaa\"" in output or \ + "compression: \"dwab\"" in output + except RuntimeError as _E: + _name, ext = os.path.splitext(file_url) + if ext in [".mxf"]: + # TODO: should't the list of allowed extensions be + # taken from an OIIO variable of supported formats + return False + else: + raise RuntimeError(_E) return False From 7755a0cf74d71d4f4f9053d3b4c404bf8e955fc0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 16 Nov 2021 11:56:42 +0100 Subject: [PATCH 20/27] nuke: fix removing adding families form plugin --- .../hosts/nuke/plugins/publish/extract_review_data_mov.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index cd918afe39..00159a81bc 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -75,13 +75,6 @@ class ExtractReviewDataMov(openpype.api.Extractor): "Baking output `{}` with settings: {}".format( o_name, o_data)) - # add additional families - add_families = o_data["add_families"] - for adf in add_families: - if adf in instance.data["families"]: - continue - instance.data["families"].append(adf) - # create exporter instance exporter = plugin.ExporterReviewMov( self, instance, o_name, o_data["extension"]) From ea76c1c346aca130a2c187143e26ff8eb156d89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 16 Nov 2021 14:45:00 +0100 Subject: [PATCH 21/27] Update openpype/lib/plugin_tools.py improving exception behavior Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/lib/plugin_tools.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index 12c77abead..891163e3ae 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -537,14 +537,14 @@ def should_decompress(file_url): "--info", "-v", file_url]) return "compression: \"dwaa\"" in output or \ "compression: \"dwab\"" in output - except RuntimeError as _E: + except RuntimeError: _name, ext = os.path.splitext(file_url) - if ext in [".mxf"]: - # TODO: should't the list of allowed extensions be - # taken from an OIIO variable of supported formats - return False - else: - raise RuntimeError(_E) + # TODO: should't the list of allowed extensions be + # taken from an OIIO variable of supported formats + if ext not in [".mxf"]: + # Reraise exception + raise + return False return False From dd27953068efe35579ae77fb3401e539ffaef041 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 18 Nov 2021 10:51:02 +0100 Subject: [PATCH 22/27] reverse commit: removing representation after all presets are used --- openpype/plugins/publish/extract_burnin.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py index b1ba4ddeac..961c7f50d1 100644 --- a/openpype/plugins/publish/extract_burnin.py +++ b/openpype/plugins/publish/extract_burnin.py @@ -307,16 +307,16 @@ class ExtractBurnin(openpype.api.Extractor): # NOTE we maybe can keep source representation if necessary instance.data["representations"].remove(repre) - self.log.debug("Files to delete: {}".format(files_to_delete)) + self.log.debug("Files to delete: {}".format(files_to_delete)) - # Delete input files - for filepath in files_to_delete: - if os.path.exists(filepath): - os.remove(filepath) - self.log.debug("Removed: \"{}\"".format(filepath)) + # Delete input files + for filepath in files_to_delete: + if os.path.exists(filepath): + os.remove(filepath) + self.log.debug("Removed: \"{}\"".format(filepath)) - if do_decompress and os.path.exists(decompressed_dir): - shutil.rmtree(decompressed_dir) + if do_decompress and os.path.exists(decompressed_dir): + shutil.rmtree(decompressed_dir) def _get_burnin_options(self): # Prepare burnin options From 0ccd1c4e0e67c00e79f90d6b6d37417aabd592eb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 24 Nov 2021 13:14:59 +0100 Subject: [PATCH 23/27] explaining how filter of preset works in comments --- .../plugins/publish/extract_review_data_mov.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 00159a81bc..37e98c0d35 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -53,21 +53,36 @@ class ExtractReviewDataMov(openpype.api.Extractor): f_families = o_data["filter"]["families"] f_task_types = o_data["filter"]["task_types"] + # test if family found in context test_families = any([ + # first if exact family set is mathing + # make sure only interesetion of list is correct bool(set(families).intersection(f_families)), + # and if famiies are set at all + # if not then return True because we want this preset + # to be active if nothig is set bool(not f_families) ]) + # test task types from filter test_task_types = any([ + # check if actual task type is defined in task types + # set in preset's filter bool(task_type in f_task_types), + # and if taskTypes are defined in preset filter + # if not then return True, because we want this filter + # to be active if no taskType is set bool(not f_task_types) ]) + # we need all filters to be positive for this + # preset to be activated test_all = all([ test_families, test_task_types ]) + # if it is not positive then skip this preset if not test_all: continue From c74de7864856c3f07e53693b5908e38442ca52c6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 24 Nov 2021 18:02:45 +0100 Subject: [PATCH 24/27] fixing representation duplicity --- .../nuke/plugins/publish/extract_review_data_mov.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 37e98c0d35..f7d0102b42 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -116,9 +116,9 @@ class ExtractReviewDataMov(openpype.api.Extractor): self.log.info(data["representations"]) - # assign to representations - instance.data["representations"] += data["representations"] + # assign to representations + instance.data["representations"] += data["representations"] - self.log.debug( - "_ representations: {}".format( - instance.data["representations"])) + self.log.debug( + "_ representations: {}".format( + instance.data["representations"])) From 005919f45c7f454f422800c47c12497e7aaa1dc4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 24 Nov 2021 18:04:26 +0100 Subject: [PATCH 25/27] fixing burnin missing key --- openpype/plugins/publish/extract_review.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e500bb361d..ba6ef17072 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -203,11 +203,12 @@ class ExtractReview(pyblish.api.InstancePlugin): new_repre["burnins"].append(str(burnin)) self.log.debug( - "Linked burnins: `{}`".format(new_repre["burnins"]) + "Linked burnins: `{}`".format(new_repre.get("burnins")) ) self.log.debug( - "New representation tags: `{}`".format(new_repre["tags"]) + "New representation tags: `{}`".format( + new_repre.get("tags")) ) temp_data = self.prepare_temp_data( From f47d74d7b43b5f77c4ebb9710ff59c7b200e05e8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 25 Nov 2021 11:28:26 +0100 Subject: [PATCH 26/27] securing backward compatibility of project locked anatomy settings --- openpype/hosts/nuke/api/plugin.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 7b8af96df0..da78883447 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -197,7 +197,15 @@ class ExporterReview(object): def get_imageio_baking_profile(self): from . import lib as opnlib nuke_imageio = opnlib.get_nuke_imageio_settings() - return nuke_imageio["baking"]["viewerProcess"] + + # TODO: this is only securing backward compatibility lets remove + # this once all projects's anotomy are upated to newer config + if "baking" in nuke_imageio.keys(): + return nuke_imageio["baking"]["viewerProcess"] + else: + return nuke_imageio["viewer"]["viewerProcess"] + + class ExporterReviewLut(ExporterReview): From 6a4f56da4ac5d37243e2a292167eca05b57d364a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 25 Nov 2021 11:48:49 +0100 Subject: [PATCH 27/27] removing developer code --- openpype/hosts/nuke/plugins/load/load_backdrop.py | 1 - openpype/hosts/nuke/plugins/publish/extract_backdrop.py | 1 - .../hosts/nuke/plugins/publish/extract_review_data_lut.py | 7 ------- .../hosts/nuke/plugins/publish/extract_review_data_mov.py | 7 ------- 4 files changed, 16 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_backdrop.py b/openpype/hosts/nuke/plugins/load/load_backdrop.py index e615af51ff..9148260e9e 100644 --- a/openpype/hosts/nuke/plugins/load/load_backdrop.py +++ b/openpype/hosts/nuke/plugins/load/load_backdrop.py @@ -4,7 +4,6 @@ import nukescripts from openpype.hosts.nuke.api import lib as pnlib from avalon.nuke import lib as anlib from avalon.nuke import containerise, update_container -reload(pnlib) class LoadBackdropNodes(api.Loader): """Loading Published Backdrop nodes (workfile, nukenodes)""" diff --git a/openpype/hosts/nuke/plugins/publish/extract_backdrop.py b/openpype/hosts/nuke/plugins/publish/extract_backdrop.py index 13f8656005..0747c15ea7 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_backdrop.py +++ b/openpype/hosts/nuke/plugins/publish/extract_backdrop.py @@ -4,7 +4,6 @@ from openpype.hosts.nuke.api import lib as pnlib import nuke import os import openpype -reload(pnlib) class ExtractBackdropNode(openpype.api.Extractor): """Extracting content of backdrop nodes diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py index d21cb0eef9..8ba746a3c4 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_lut.py @@ -4,13 +4,6 @@ from avalon.nuke import lib as anlib from openpype.hosts.nuke.api import plugin import openpype -try: - from __builtin__ import reload -except ImportError: - from importlib import reload - -reload(plugin) - class ExtractReviewDataLut(openpype.api.Extractor): """Extracts movie and thumbnail with baked in luts diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index f7d0102b42..b5890b5c51 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -4,13 +4,6 @@ from avalon.nuke import lib as anlib from openpype.hosts.nuke.api import plugin import openpype -try: - from __builtin__ import reload -except ImportError: - from importlib import reload - -reload(plugin) - class ExtractReviewDataMov(openpype.api.Extractor): """Extracts movie and thumbnail with baked in luts