From ade2a26e84b80c01fd3ea4b39bc216b483f786ab Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 8 Jan 2020 00:02:14 +0100 Subject: [PATCH] feat(nuke): adding back baking mov from nuke --- pype/nuke/lib.py | 275 ++++++++++++++---- .../global/publish/collect_filesequences.py | 2 + .../nuke/publish/extract_review_data_lut.py | 3 +- .../nuke/publish/extract_review_data_mov.py | 57 ++++ .../nuke/publish/extract_review_mov.py | 181 ------------ 5 files changed, 273 insertions(+), 245 deletions(-) create mode 100644 pype/plugins/nuke/publish/extract_review_data_mov.py delete mode 100644 pype/plugins/nuke/publish/extract_review_mov.py diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 12a083eca1..9201e9c63e 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -1199,13 +1199,13 @@ class BuildWorkfile(WorkfileSettings): self.ypos -= (self.ypos_size * multiply) + self.ypos_gap -class Exporter_review_lut: +class Exporter_review: """ - Generator object for review lut from Nuke + Base class object for generating review data from Nuke Args: klass (pyblish.plugin): pyblish plugin parent - + instance (pyblish.context.instance): """ _temp_nodes = [] @@ -1213,6 +1213,101 @@ class Exporter_review_lut: "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) + else: + self.fname = os.path.basename(self.path_in) + self.fhead = os.path.splitext(self.fname)[0] + "." + self.first_frame = self.instance.data.get("frameStart", None) + self.last_frame = self.instance.data.get("frameEnd", 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, + "anatomy_template": "publish", + "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 [n for n in nuke.allNodes() + if "Viewer" in n.Class()]: + 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 + + +class Exporter_review_lut(Exporter_review): + """ + Generator object for review lut from Nuke + + Args: + klass (pyblish.plugin): pyblish plugin parent + + + """ def __init__(self, klass, instance, @@ -1221,9 +1316,8 @@ class Exporter_review_lut: cube_size=None, lut_size=None, lut_style=None): - - self.log = klass.log - self.instance = instance + # initialize parent class + Exporter_review.__init__(self, klass, instance) self.name = name or "baked_lut" self.ext = ext or "cube" @@ -1231,16 +1325,13 @@ class Exporter_review_lut: self.lut_size = lut_size or 1024 self.lut_style = lut_style or "linear" - self.stagingDir = self.instance.data["stagingDir"] - self.collection = self.instance.data.get("collection", None) - # 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.stagingDir, self.file).replace("\\", "/") + self.path = os.path.join(self.staging_dir, self.file).replace("\\", "/") def generate_lut(self): # ---------- start nodes creation @@ -1303,70 +1394,128 @@ class Exporter_review_lut: return self.data - 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) +class Exporter_review_mov(Exporter_review): + """ + Metaclass for generating review mov files + + Args: + klass (pyblish.plugin): pyblish plugin parent + + + """ + def __init__(self, + klass, + instance, + name=None, + ext=None, + ): + # initialize parent class + Exporter_review.__init__(self, klass, instance) + + # passing presets for nodes to self + if hasattr(klass, "nodes"): + self.nodes = klass.nodes else: - self.fname = os.path.basename(self.instance.data.get("path", None)) - self.fhead = os.path.splitext(self.fname)[0] + "." - self.first_frame = self.instance.data.get("frameStart", None) - self.last_frame = self.instance.data.get("frameEnd", None) + self.nodes = {} - if "#" in self.fhead: - self.fhead = self.fhead.replace("#", "")[:-1] + self.name = name or "baked" + self.ext = ext or "mov" - def get_representation_data(self): + # set frame start / end and file name to self + self.get_file_info() - repre = { - 'name': self.name, - 'ext': self.ext, - 'files': self.file, - "stagingDir": self.stagingDir, - "anatomy_template": "publish", - "tags": [self.name.replace("_", "-")] - } + self.log.info("File info was set...") - self.data["representations"].append(repre) + self.file = self.fhead + self.name + ".{}".format(self.ext) + self.path = os.path.join(self.staging_dir, self.file).replace("\\", "/") - def get_view_process_node(self): - """ - Will get any active view process. + def generate_mov(self, farm=False): + # ---------- start nodes creation - Arguments: - self (class): in object definition + # 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) + # connect + self._temp_nodes.append(r_node) + self.previous_node = r_node + self.log.debug("Read... `{}`".format(self._temp_nodes)) - Returns: - nuke.Node: copy node of Input Process node - """ - anlib.reset_selection() - ipn_orig = None - for v in [n for n in nuke.allNodes() - if "Viewer" in n.Class()]: - 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) + # 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 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() + # reformat_node = nuke.createNode("Reformat") + # rn_preset = self.nodes.get("Reformat", None) + # if rn_preset: + # self.log.debug("Reformat preset") + # for k, v in rn_preset: + # self.log.debug("k, v: {0}:{1}".format(k, v)) + # if isinstance(v, unicode): + # v = str(v) + # reformat_node[k].setValue(v) + # # connect + # reformat_node.setInput(0, self.previous_node) + # self._temp_nodes.append(reformat_node) + # self.previous_node = reformat_node + # self.log.debug("Reformat... `{}`".format(self._temp_nodes)) + + # OCIODisplay node + 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)) + self.instance.data["baked_colorspace_movie"] = self.path + write_node["file"].setValue(self.path) + write_node["file_type"].setValue(self.ext) + 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 + + if not farm: + self.log.info("Rendering... ") + # Render Write node + nuke.execute( + write_node.name(), + int(self.first_frame), + int(self.last_frame)) + + self.log.info("Rendered...") + + # ---------- generate representation data + self.get_representation_data( + tags=["review", "delete"], + range=True + ) + + self.log.debug("Representation... `{}`".format(self.data)) + + # ---------- Clean up + # for node in self._temp_nodes: + # nuke.delete(node) + # self.log.info("Deleted nodes...") + + return self.data - return ipn def get_dependent_nodes(nodes): """Get all dependent nodes connected to the list of nodes. diff --git a/pype/plugins/global/publish/collect_filesequences.py b/pype/plugins/global/publish/collect_filesequences.py index e658cd434c..6a59f5dffc 100644 --- a/pype/plugins/global/publish/collect_filesequences.py +++ b/pype/plugins/global/publish/collect_filesequences.py @@ -148,6 +148,8 @@ class CollectRenderedFrames(pyblish.api.ContextPlugin): os.environ.update(session) instance = metadata.get("instance") if instance: + # here is the place to add ability for nuke noninteractive + # ______________________________________ instance_family = instance.get("family") pixel_aspect = instance.get("pixelAspect", 1) resolution_width = instance.get("resolutionWidth", 1920) diff --git a/pype/plugins/nuke/publish/extract_review_data_lut.py b/pype/plugins/nuke/publish/extract_review_data_lut.py index dfc10952cd..f5fc3e59db 100644 --- a/pype/plugins/nuke/publish/extract_review_data_lut.py +++ b/pype/plugins/nuke/publish/extract_review_data_lut.py @@ -6,7 +6,7 @@ import pype reload(pnlib) -class ExtractReviewLutData(pype.api.Extractor): +class ExtractReviewDataLut(pype.api.Extractor): """Extracts movie and thumbnail with baked in luts must be run after extract_render_local.py @@ -37,6 +37,7 @@ class ExtractReviewLutData(pype.api.Extractor): self.log.info( "StagingDir `{0}`...".format(instance.data["stagingDir"])) + # generate data with anlib.maintained_selection(): exporter = pnlib.Exporter_review_lut( self, instance diff --git a/pype/plugins/nuke/publish/extract_review_data_mov.py b/pype/plugins/nuke/publish/extract_review_data_mov.py new file mode 100644 index 0000000000..585bd3f108 --- /dev/null +++ b/pype/plugins/nuke/publish/extract_review_data_mov.py @@ -0,0 +1,57 @@ +import os +import nuke +import pyblish.api +from avalon.nuke import lib as anlib +from pype.nuke import lib as pnlib +import pype +reload(pnlib) + + +class ExtractReviewDataMov(pype.api.Extractor): + """Extracts movie and thumbnail with baked in luts + + must be run after extract_render_local.py + + """ + + order = pyblish.api.ExtractorOrder + 0.01 + label = "Extract Review Data Mov" + + families = ["review"] + hosts = ["nuke"] + + def process(self, instance): + families = instance.data["families"] + self.log.info("Creating staging dir...") + if "representations" in instance.data: + staging_dir = instance.data[ + "representations"][0]["stagingDir"].replace("\\", "/") + instance.data["stagingDir"] = staging_dir + instance.data["representations"][0]["tags"] = [] + else: + instance.data["representations"] = [] + # get output path + render_path = instance.data['path'] + staging_dir = os.path.normpath(os.path.dirname(render_path)) + instance.data["stagingDir"] = staging_dir + + self.log.info( + "StagingDir `{0}`...".format(instance.data["stagingDir"])) + + # generate data + with anlib.maintained_selection(): + exporter = pnlib.Exporter_review_mov( + self, instance) + + if "render.farm" in families: + instance.data["families"].remove("review") + instance.data["families"].remove("ftrack") + data = exporter.generate_mov(farm=True) + else: + data = exporter.generate_mov() + + # assign to representations + instance.data["representations"] += data["representations"] + + self.log.debug( + "_ representations: {}".format(instance.data["representations"])) diff --git a/pype/plugins/nuke/publish/extract_review_mov.py b/pype/plugins/nuke/publish/extract_review_mov.py deleted file mode 100644 index ed3101951c..0000000000 --- a/pype/plugins/nuke/publish/extract_review_mov.py +++ /dev/null @@ -1,181 +0,0 @@ -import os -import nuke -import pyblish.api -import pype\ - -class ExtractReviewData(pype.api.Extractor): - """Extracts movie and thumbnail with baked in luts - - must be run after extract_render_local.py - - """ - - order = pyblish.api.ExtractorOrder + 0.01 - label = "Extract Review Data" - - families = ["review"] - hosts = ["nuke"] - - def process(self, instance): - - # Store selection - selection = [i for i in nuke.allNodes() if i["selected"].getValue()] - # Deselect all nodes to prevent external connections - [i["selected"].setValue(False) for i in nuke.allNodes()] - self.log.debug("creating staging dir:") - self.staging_dir(instance) - - self.log.debug("instance: {}".format(instance)) - self.log.debug("instance.data[families]: {}".format( - instance.data["families"])) - - self.render_review_representation(instance, representation="mov") - - # Restore selection - [i["selected"].setValue(False) for i in nuke.allNodes()] - [i["selected"].setValue(True) for i in selection] - - def render_review_representation(self, - instance, - representation="mov"): - - assert instance.data['representations'][0]['files'], "Instance data files should't be empty!" - - temporary_nodes = [] - stagingDir = instance.data[ - 'representations'][0]["stagingDir"].replace("\\", "/") - self.log.debug("StagingDir `{0}`...".format(stagingDir)) - - collection = instance.data.get("collection", None) - - if collection: - # get path - fname = os.path.basename(collection.format( - "{head}{padding}{tail}")) - fhead = collection.format("{head}") - - # get first and last frame - first_frame = min(collection.indexes) - last_frame = max(collection.indexes) - else: - fname = os.path.basename(instance.data.get("path", None)) - fhead = os.path.splitext(fname)[0] + "." - first_frame = instance.data.get("frameStart", None) - last_frame = instance.data.get("frameEnd", None) - - rnode = nuke.createNode("Read") - - rnode["file"].setValue( - os.path.join(stagingDir, fname).replace("\\", "/")) - - rnode["first"].setValue(first_frame) - rnode["origfirst"].setValue(first_frame) - rnode["last"].setValue(last_frame) - rnode["origlast"].setValue(last_frame) - temporary_nodes.append(rnode) - previous_node = rnode - - # get input process and connect it to baking - ipn = self.get_view_process_node() - if ipn is not None: - ipn.setInput(0, previous_node) - previous_node = ipn - temporary_nodes.append(ipn) - - reformat_node = nuke.createNode("Reformat") - - ref_node = self.nodes.get("Reformat", None) - if ref_node: - for k, v in ref_node: - self.log.debug("k,v: {0}:{1}".format(k,v)) - if isinstance(v, unicode): - v = str(v) - reformat_node[k].setValue(v) - - reformat_node.setInput(0, previous_node) - previous_node = reformat_node - temporary_nodes.append(reformat_node) - - dag_node = nuke.createNode("OCIODisplay") - dag_node.setInput(0, previous_node) - previous_node = dag_node - temporary_nodes.append(dag_node) - - # create write node - write_node = nuke.createNode("Write") - - if representation in "mov": - file = fhead + "baked.mov" - name = "baked" - path = os.path.join(stagingDir, file).replace("\\", "/") - self.log.debug("Path: {}".format(path)) - instance.data["baked_colorspace_movie"] = path - write_node["file"].setValue(path) - write_node["file_type"].setValue("mov") - write_node["raw"].setValue(1) - write_node.setInput(0, previous_node) - temporary_nodes.append(write_node) - tags = ["review", "delete"] - - elif representation in "jpeg": - file = fhead + "jpeg" - name = "thumbnail" - path = os.path.join(stagingDir, file).replace("\\", "/") - instance.data["thumbnail"] = path - write_node["file"].setValue(path) - write_node["file_type"].setValue("jpeg") - write_node["raw"].setValue(1) - write_node.setInput(0, previous_node) - temporary_nodes.append(write_node) - tags = ["thumbnail"] - - # retime for - first_frame = int(last_frame) / 2 - last_frame = int(last_frame) / 2 - - repre = { - 'name': name, - 'ext': representation, - 'files': file, - "stagingDir": stagingDir, - "frameStart": first_frame, - "frameEnd": last_frame, - "anatomy_template": "render", - "tags": tags - } - instance.data["representations"].append(repre) - - # Render frames - nuke.execute(write_node.name(), int(first_frame), int(last_frame)) - - self.log.debug("representations: {}".format(instance.data["representations"])) - - # Clean up - for node in temporary_nodes: - nuke.delete(node) - - def get_view_process_node(self): - - # Select only the target node - if nuke.selectedNodes(): - [n.setSelected(False) for n in nuke.selectedNodes()] - - ipn_orig = None - for v in [n for n in nuke.allNodes() - if "Viewer" in n.Class()]: - 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: - nuke.nodeCopy('%clipboard%') - - [n.setSelected(False) for n in nuke.selectedNodes()] # Deselect all - - nuke.nodePaste('%clipboard%') - - ipn = nuke.selectedNode() - - return ipn