From f350d7694cf5c4d7c2598aa540db219cbf216403 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Dec 2019 17:26:23 +0100 Subject: [PATCH 01/30] feat(global): print ffmpeg path --- pype/plugins/global/publish/validate_ffmpeg_installed.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/plugins/global/publish/validate_ffmpeg_installed.py b/pype/plugins/global/publish/validate_ffmpeg_installed.py index 6d5ffba1e1..df7c330e95 100644 --- a/pype/plugins/global/publish/validate_ffmpeg_installed.py +++ b/pype/plugins/global/publish/validate_ffmpeg_installed.py @@ -27,6 +27,8 @@ class ValidateFfmpegInstallef(pyblish.api.Validator): return True def process(self, instance): + self.log.info("ffmpeg path: `{}`".format( + os.environ.get("FFMPEG_PATH", ""))) if self.is_tool( os.path.join( os.environ.get("FFMPEG_PATH", ""), "ffmpeg")) is False: From 346e3e27bce2bc0dd71829a3a686857cdd1268d7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Dec 2019 17:27:39 +0100 Subject: [PATCH 02/30] feat(nuke): extract review will generate lut file this will remove the nuke rendering of `*.baked..mov` --- .../nuke/publish/extract_review_data_lut.py | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 pype/plugins/nuke/publish/extract_review_data_lut.py diff --git a/pype/plugins/nuke/publish/extract_review_data_lut.py b/pype/plugins/nuke/publish/extract_review_data_lut.py new file mode 100644 index 0000000000..bba9544b13 --- /dev/null +++ b/pype/plugins/nuke/publish/extract_review_data_lut.py @@ -0,0 +1,185 @@ +import os +import nuke +import pyblish.api +from avalon.nuke import lib as anlib +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 Lut" + + families = ["review"] + hosts = ["nuke"] + + def process(self, instance): + + self.log.debug("creating staging dir:") + self.staging_dir(instance) + + with anlib.maintained_selection(): + if "still" not in instance.data["families"]: + self.render_review_representation(instance, + representation="mov") + self.render_review_representation(instance, + representation="jpeg") + else: + self.render_review_representation(instance, representation="jpeg") + + 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): + """ + Will get any active view process. + + Arguments: + self (class): in object definition + + Returns: + nuke.Node: copy node of Input Process node + """ + + with anlib.maintained_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 From 48e5c4fd26143d78903564679f6dce15f5239da3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 5 Dec 2019 10:36:08 +0100 Subject: [PATCH 03/30] feat(nuke): Lut exporter added to nuke.lib --- pype/nuke/lib.py | 169 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 157af9019d..960b65f769 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -6,6 +6,7 @@ from collections import OrderedDict from avalon import api, io, lib import avalon.nuke +from avalon.nuke import lib as anlib import pype.api as pype import nuke @@ -1190,3 +1191,171 @@ class BuildWorkfile(WorkfileSettings): def position_up(self, multiply=1): self.ypos -= (self.ypos_size * multiply) + self.ypos_gap + + +class Exporter_review_lut: + """ + Generator object for review lut from Nuke + + Args: + klass (pyblish.plugin): pyblish plugin parent + + + """ + _temp_nodes = [] + data = dict({ + "representations": list() + }) + + def __init__(self, + klass, + instance, + name=None, + ext=None, + lut_size=None, + lut_style=None): + + self.log = klass.log + self.instance = instance + + self.name = name or "baked_lut" + self.ext = ext or "cube" + 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("\\", "/") + + def generate_lut(self): + # ---------- start nodes creation + + # CMSTestPattern + cms_node = nuke.createNode("CMSTestPattern") + cms_node["cube_size"].setValue(96) + # 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)) + + # 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 + for node in self._temp_nodes: + nuke.delete(node) + self.log.info("Deleted nodes...") + + 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) + 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) + + if "#" in self.fhead: + self.fhead = self.fhead.replace("#", "")[:-1] + + def get_representation_data(self): + + repre = { + 'name': self.name, + 'ext': self.ext, + 'files': self.file, + "stagingDir": self.stagingDir, + "anatomy_template": "publish", + "tags": [self.name.replace("_", "-")] + } + + 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 From 49eadd8a2d8c28e73b9ef7ffc63fb12d3c87d1f6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 5 Dec 2019 10:36:53 +0100 Subject: [PATCH 04/30] feat(global): added lut filter to ffmpeg --- pype/plugins/global/publish/extract_review.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index de167710a5..3ff3241812 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -162,6 +162,13 @@ class ExtractReview(pyblish.api.InstancePlugin): # output filename output_args.append(full_output_path) + + lut_path = instance.data.get("lutPath") + if lut_path: + lut_arg = "-vf \"lut3d=file='{}'\"".format( + lut_path) + output_args.insert(0, lut_arg) + mov_args = [ os.path.join( os.environ.get( From bd5805301b3c234f50fe8901f5a3f6a59dd09dac Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 5 Dec 2019 10:37:32 +0100 Subject: [PATCH 05/30] feat(nuke): lut extractor added to nuke plugins --- .../nuke/publish/extract_review_data_lut.py | 182 +++--------------- 1 file changed, 26 insertions(+), 156 deletions(-) diff --git a/pype/plugins/nuke/publish/extract_review_data_lut.py b/pype/plugins/nuke/publish/extract_review_data_lut.py index bba9544b13..54013af11a 100644 --- a/pype/plugins/nuke/publish/extract_review_data_lut.py +++ b/pype/plugins/nuke/publish/extract_review_data_lut.py @@ -1,185 +1,55 @@ 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 ExtractReviewData(pype.api.Extractor): +class ExtractReviewLutData(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 + order = pyblish.api.ExtractorOrder + 0.005 label = "Extract Review Data Lut" families = ["review"] hosts = ["nuke"] def process(self, instance): + self.log.debug( + "_ representations: {}".format(instance.data["representations"])) - self.log.debug("creating staging dir:") - self.staging_dir(instance) + self.log.info("Creating staging dir...") - with anlib.maintained_selection(): - if "still" not in instance.data["families"]: - self.render_review_representation(instance, - representation="mov") - self.render_review_representation(instance, - representation="jpeg") - else: - self.render_review_representation(instance, representation="jpeg") - - 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)) + instance.data["stagingDir"] = stagingDir - collection = instance.data.get("collection", None) + instance.data['representations'][0]["tags"] = ["review"] - if collection: - # get path - fname = os.path.basename(collection.format( - "{head}{padding}{tail}")) - fhead = collection.format("{head}") + self.log.info( + "StagingDir `{0}`...".format(instance.data["stagingDir"])) - # 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): - """ - Will get any active view process. - - Arguments: - self (class): in object definition - - Returns: - nuke.Node: copy node of Input Process node - """ + if "representations" not in instance.data: + instance.data["representations"] = [] with anlib.maintained_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) + exporter = pnlib.Exporter_review_lut( + self, instance + ) + data = exporter.generate_lut() - 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() + # assign to representations + instance.data["lutPath"] = os.path.join( + exporter.stagingDir, exporter.file).replace("\\", "/").replace( + "C:/", "C\\:/") + instance.data["representations"] += data["representations"] - return ipn + self.log.debug( + "_ lutPath: {}".format(instance.data["lutPath"])) + self.log.debug( + "_ representations: {}".format(instance.data["representations"])) From bf8a829eee011b9d5fecb8bb4781a2a44395f1bd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 5 Dec 2019 10:38:06 +0100 Subject: [PATCH 06/30] fix(nuke): review extractor fixed maintained selection --- .../nuke/publish/extract_review_data.py | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/pype/plugins/nuke/publish/extract_review_data.py b/pype/plugins/nuke/publish/extract_review_data.py index 791b9d7969..9bb4f93582 100644 --- a/pype/plugins/nuke/publish/extract_review_data.py +++ b/pype/plugins/nuke/publish/extract_review_data.py @@ -1,5 +1,6 @@ import os import nuke +from avalon.nuke import lib as anlib import pyblish.api import pype @@ -18,28 +19,22 @@ class ExtractReviewData(pype.api.Extractor): 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) + with anlib.maintained_selection(): + 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.log.debug("instance: {}".format(instance)) + self.log.debug("instance.data[families]: {}".format( + instance.data["families"])) - if "still" not in instance.data["families"]: - self.render_review_representation(instance, - representation="mov") - self.render_review_representation(instance, - representation="jpeg") - else: + # if "still" not in instance.data["families"]: + # self.render_review_representation(instance, + # representation="mov") + # self.render_review_representation(instance, + # representation="jpeg") + # else: self.render_review_representation(instance, representation="jpeg") - # 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, @@ -69,6 +64,9 @@ class ExtractReviewData(pype.api.Extractor): first_frame = instance.data.get("frameStart", None) last_frame = instance.data.get("frameEnd", None) + if "#" in fhead: + fhead = fhead.replace("#", "")[:-1] + rnode = nuke.createNode("Read") rnode["file"].setValue( From d3e36f13bc01b7234b492748653572bc5d9d3cb9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 5 Dec 2019 10:38:42 +0100 Subject: [PATCH 07/30] feat(global): adding debug prints to assumed destination --- pype/plugins/global/publish/collect_templates.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/plugins/global/publish/collect_templates.py b/pype/plugins/global/publish/collect_templates.py index b80ca4ae1b..9b0c03fdee 100644 --- a/pype/plugins/global/publish/collect_templates.py +++ b/pype/plugins/global/publish/collect_templates.py @@ -85,3 +85,6 @@ class CollectTemplates(pyblish.api.InstancePlugin): instance.data["assumedDestination"] = os.path.dirname( (anatomy.format(template_data))["publish"]["path"] ) + self.log.info("Assumed Destination has been created...") + self.log.debug("__ assumedTemplateData: `{}`".format(instance.data["assumedTemplateData"])) + self.log.debug("__ template: `{}`".format(instance.data["template"])) From 1361d12452223977a14cb623b1765457ca2ea54b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 07:59:19 +0100 Subject: [PATCH 08/30] feat(nuke): polishing the Lut Exporter --- pype/nuke/lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 960b65f769..6349f35bea 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -1212,6 +1212,7 @@ class Exporter_review_lut: instance, name=None, ext=None, + cube_size=None, lut_size=None, lut_style=None): @@ -1220,6 +1221,7 @@ class Exporter_review_lut: 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" @@ -1239,7 +1241,7 @@ class Exporter_review_lut: # CMSTestPattern cms_node = nuke.createNode("CMSTestPattern") - cms_node["cube_size"].setValue(96) + cms_node["cube_size"].setValue(self.cube_size) # connect self._temp_nodes.append(cms_node) self.previous_node = cms_node From 43ed9443941a136227aa694e1fd7cc84985b7ce6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 08:00:15 +0100 Subject: [PATCH 09/30] feat(global): implementing Lut integration into Extract Review --- pype/plugins/global/publish/extract_review.py | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 3ff3241812..59ef308f9a 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -165,9 +165,35 @@ class ExtractReview(pyblish.api.InstancePlugin): lut_path = instance.data.get("lutPath") if lut_path: - lut_arg = "-vf \"lut3d=file='{}'\"".format( + # removing Gama info as it is all baked in lut + gamma = next((g for g in input_args + if "-gamma" in g), None) + if gamma: + input_args.remove(gamma) + + # find all video format settings + vf_settings = [p for p in output_args + for v in ["-filter:v", "-vf"] + if v in p] + self.log.debug("_ vf_settings: `{}`".format(vf_settings)) + # remove them from output args list + for p in vf_settings: + self.log.debug("_ remove p: `{}`".format(p)) + output_args.remove(p) + self.log.debug("_ output_args: `{}`".format(output_args)) + # strip them from all flags + vf_fixed = [p.replace("-vf ", "").replace("-filter:v ", "") for p in vf_settings] + # create lut argument + lut_arg = "lut3d=file='{}',colormatrix=bt601:bt709".format( lut_path) - output_args.insert(0, lut_arg) + vf_fixed.insert(0, lut_arg) + # create new video filter setting + vf_back = "-vf " + ",".join(vf_fixed) + # add it to output_args + output_args.insert(0, vf_back) + self.log.info("Added Lut to ffmpeg command") + self.log.debug("_ output_args: `{}`".format(output_args)) + mov_args = [ os.path.join( From ec9f5b5e0405e0ba53b26d1d14adbcb78d680c20 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 08:00:35 +0100 Subject: [PATCH 10/30] feat(nuke): adding format data to instance --- pype/plugins/nuke/publish/collect_instances.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index 483f260295..cb98b8244d 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -23,6 +23,8 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): instances = [] # creating instances per write node + root = nuke.root() + self.log.debug("nuke.allNodes(): {}".format(nuke.allNodes())) for node in nuke.allNodes(): @@ -61,7 +63,13 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): family = avalon_knob_data["family"] families = [avalon_knob_data["families"]] - + + # Get format + format = root['format'].value() + resolution_width = format.width() + resolution_height = format.height() + pixel_aspect = format.pixelAspect() + if node.Class() not in "Read": if node["render"].value(): self.log.info("flagged for render") @@ -87,7 +95,10 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): "avalonKnob": avalon_knob_data, "publish": node.knob('publish').value(), "step": 1, - "fps": nuke.root()['fps'].value() + "fps": nuke.root()['fps'].value(), + "resolutionWidth": resolution_width, + "resolutionHeight": resolution_height, + "pixelAspect": pixel_aspect, }) @@ -95,5 +106,4 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): instances.append(instance) context.data["instances"] = instances - self.log.debug("context: {}".format(context)) From 3c90a7984023889b30c2fc7b0233b02c8a6a3bb3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 13:58:31 +0100 Subject: [PATCH 11/30] feat(global): extr review pixel aspect rescale --- pype/plugins/global/publish/extract_review.py | 76 ++++++++++++++----- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 59ef308f9a..fa02826527 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -151,7 +151,6 @@ class ExtractReview(pyblish.api.InstancePlugin): output_args.extend(profile.get('output', [])) # letter_box - # TODO: add to documentation lb = profile.get('letter_box', None) if lb: output_args.append( @@ -163,6 +162,18 @@ class ExtractReview(pyblish.api.InstancePlugin): # output filename output_args.append(full_output_path) + # scaling none square pixels and 1920 width + # scale=320:-2 # to auto count height with output to be multiple of 2 + if profile.get('reformat', False): + pixel_aspect = instance.data["pixelAspect"] + scaling_arg = "scale=1920:'ceil((1920/{})/2)*2':flags=lanczos,setsar=1".format( + pixel_aspect) + vf_back = self.add_video_filter_args( + output_args, scaling_arg) + # add it to output_args + output_args.insert(0, vf_back) + + # baking lut file application lut_path = instance.data.get("lutPath") if lut_path: # removing Gama info as it is all baked in lut @@ -171,24 +182,15 @@ class ExtractReview(pyblish.api.InstancePlugin): if gamma: input_args.remove(gamma) - # find all video format settings - vf_settings = [p for p in output_args - for v in ["-filter:v", "-vf"] - if v in p] - self.log.debug("_ vf_settings: `{}`".format(vf_settings)) - # remove them from output args list - for p in vf_settings: - self.log.debug("_ remove p: `{}`".format(p)) - output_args.remove(p) - self.log.debug("_ output_args: `{}`".format(output_args)) - # strip them from all flags - vf_fixed = [p.replace("-vf ", "").replace("-filter:v ", "") for p in vf_settings] # create lut argument - lut_arg = "lut3d=file='{}',colormatrix=bt601:bt709".format( - lut_path) - vf_fixed.insert(0, lut_arg) - # create new video filter setting - vf_back = "-vf " + ",".join(vf_fixed) + lut_arg = "lut3d=file='{}'".format( + lut_path.replace( + "\\", "/").replace(":/", "\\:/") + ) + lut_arg += ",colormatrix=bt601:bt709" + + vf_back = self.add_video_filter_args( + output_args, lut_arg) # add it to output_args output_args.insert(0, vf_back) self.log.info("Added Lut to ffmpeg command") @@ -240,3 +242,41 @@ class ExtractReview(pyblish.api.InstancePlugin): instance.data["representations"] = representations_new self.log.debug("Families Out: `{}`".format(instance.data["families"])) + + + def add_video_filter_args(self, args, inserting_arg): + """ + Fixing video filter argumets to be one long string + + Args: + args (list): list of string arguments + inserting_arg (str): string argument we want to add + (without flag `-vf`) + + Returns: + str: long joined argument to be added back to list of arguments + + """ + # find all video format settings + vf_settings = [p for p in args + for v in ["-filter:v", "-vf"] + if v in p] + self.log.debug("_ vf_settings: `{}`".format(vf_settings)) + + # remove them from output args list + for p in vf_settings: + self.log.debug("_ remove p: `{}`".format(p)) + args.remove(p) + self.log.debug("_ args: `{}`".format(args)) + + # strip them from all flags + vf_fixed = [p.replace("-vf ", "").replace("-filter:v ", "") + for p in vf_settings] + + self.log.debug("_ vf_fixed: `{}`".format(vf_fixed)) + vf_fixed.insert(0, inserting_arg) + self.log.debug("_ vf_fixed: `{}`".format(vf_fixed)) + # create new video filter setting + vf_back = "-vf " + ",".join(vf_fixed) + + return vf_back From b79e22464b02c0b3c4ecf7224bac69ae84270ac6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 13:59:35 +0100 Subject: [PATCH 12/30] fix(nuke: when writes collected family should be `write` otherwise validate frames is picking it up --- pype/plugins/nuke/publish/collect_writes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/nuke/publish/collect_writes.py b/pype/plugins/nuke/publish/collect_writes.py index ba8a0534b1..5484d971bf 100644 --- a/pype/plugins/nuke/publish/collect_writes.py +++ b/pype/plugins/nuke/publish/collect_writes.py @@ -99,7 +99,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): "subset": instance.data["subset"], "fps": instance.context.data["fps"] } - + instance.data["family"] = "write" group_node = [x for x in instance if x.Class() == "Group"][0] deadlineChunkSize = 1 if "deadlineChunkSize" in group_node.knobs(): From 78f2da25cf81c494ee6f7d1979ddf98ab67784d6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 14:01:42 +0100 Subject: [PATCH 13/30] feat(nuke): extract thumbnail from before Review Data (mov, jpg) --- ...ct_review_data.py => extract_thumbnail.py} | 69 ++++++------------- 1 file changed, 22 insertions(+), 47 deletions(-) rename pype/plugins/nuke/publish/{extract_review_data.py => extract_thumbnail.py} (67%) diff --git a/pype/plugins/nuke/publish/extract_review_data.py b/pype/plugins/nuke/publish/extract_thumbnail.py similarity index 67% rename from pype/plugins/nuke/publish/extract_review_data.py rename to pype/plugins/nuke/publish/extract_thumbnail.py index 9bb4f93582..5740a90924 100644 --- a/pype/plugins/nuke/publish/extract_review_data.py +++ b/pype/plugins/nuke/publish/extract_thumbnail.py @@ -4,7 +4,7 @@ from avalon.nuke import lib as anlib import pyblish.api import pype -class ExtractReviewData(pype.api.Extractor): +class ExtractThumbnail(pype.api.Extractor): """Extracts movie and thumbnail with baked in luts must be run after extract_render_local.py @@ -12,7 +12,7 @@ class ExtractReviewData(pype.api.Extractor): """ order = pyblish.api.ExtractorOrder + 0.01 - label = "Extract Review Data" + label = "Extract Thumbnail" families = ["review"] hosts = ["nuke"] @@ -20,29 +20,19 @@ class ExtractReviewData(pype.api.Extractor): def process(self, instance): with anlib.maintained_selection(): - 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"])) - # if "still" not in instance.data["families"]: - # self.render_review_representation(instance, - # representation="mov") - # self.render_review_representation(instance, - # representation="jpeg") - # else: - self.render_review_representation(instance, representation="jpeg") + self.render_thumbnail(instance) - def render_review_representation(self, - instance, - representation="mov"): + def render_thumbnail(self, instance): assert instance.data['representations'][0]['files'], "Instance data files should't be empty!" temporary_nodes = [] + self.log.info("Getting staging dir...") stagingDir = instance.data[ 'representations'][0]["stagingDir"].replace("\\", "/") self.log.debug("StagingDir `{0}`...".format(stagingDir)) @@ -107,39 +97,24 @@ class ExtractReviewData(pype.api.Extractor): # create write node write_node = nuke.createNode("Write") + 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"] - 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 + # retime for + first_frame = int(last_frame) / 2 + last_frame = int(last_frame) / 2 repre = { 'name': name, - 'ext': representation, + 'ext': "jpeg", 'files': file, "stagingDir": stagingDir, "frameStart": first_frame, @@ -154,9 +129,9 @@ class ExtractReviewData(pype.api.Extractor): self.log.debug("representations: {}".format(instance.data["representations"])) - # Clean up - for node in temporary_nodes: - nuke.delete(node) + # # Clean up + # for node in temporary_nodes: + # nuke.delete(node) def get_view_process_node(self): From 5754450b88e7cb5b894137d5daac0608df77ccfb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 14:02:37 +0100 Subject: [PATCH 14/30] fix(nuke): the path was only working with C: Also the replace was moved to Extract Review --- pype/plugins/nuke/publish/extract_review_data_lut.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/plugins/nuke/publish/extract_review_data_lut.py b/pype/plugins/nuke/publish/extract_review_data_lut.py index 54013af11a..910b6ee19e 100644 --- a/pype/plugins/nuke/publish/extract_review_data_lut.py +++ b/pype/plugins/nuke/publish/extract_review_data_lut.py @@ -45,8 +45,7 @@ class ExtractReviewLutData(pype.api.Extractor): # assign to representations instance.data["lutPath"] = os.path.join( - exporter.stagingDir, exporter.file).replace("\\", "/").replace( - "C:/", "C\\:/") + exporter.stagingDir, exporter.file).replace("\\", "/") instance.data["representations"] += data["representations"] self.log.debug( From f4a0b341ac5036945a9f47f0ebfe50cb030d564b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 14:03:16 +0100 Subject: [PATCH 15/30] fix(nuke): validator had wrong family --- pype/plugins/nuke/publish/validate_rendered_frames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/nuke/publish/validate_rendered_frames.py b/pype/plugins/nuke/publish/validate_rendered_frames.py index e244a9b4b6..3887b5d5b7 100644 --- a/pype/plugins/nuke/publish/validate_rendered_frames.py +++ b/pype/plugins/nuke/publish/validate_rendered_frames.py @@ -28,7 +28,7 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin): """ Validates file output. """ order = pyblish.api.ValidatorOrder + 0.1 - families = ["render.no"] + families = ["render"] label = "Validate rendered frame" hosts = ["nuke", "nukestudio"] From 350dbda38ccf8d3ebf7c9322d5fa2e4a6b9cb000 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 14:41:50 +0100 Subject: [PATCH 16/30] feat(global): atatching conditions to preset tags instead of arguments that is what they are for, isnt it? ;) --- pype/plugins/nuke/publish/extract_thumbnail.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pype/plugins/nuke/publish/extract_thumbnail.py b/pype/plugins/nuke/publish/extract_thumbnail.py index 5740a90924..126203603e 100644 --- a/pype/plugins/nuke/publish/extract_thumbnail.py +++ b/pype/plugins/nuke/publish/extract_thumbnail.py @@ -4,6 +4,7 @@ from avalon.nuke import lib as anlib import pyblish.api import pype + class ExtractThumbnail(pype.api.Extractor): """Extracts movie and thumbnail with baked in luts @@ -26,9 +27,7 @@ class ExtractThumbnail(pype.api.Extractor): self.render_thumbnail(instance) - def render_thumbnail(self, instance): - assert instance.data['representations'][0]['files'], "Instance data files should't be empty!" temporary_nodes = [] @@ -129,9 +128,9 @@ class ExtractThumbnail(pype.api.Extractor): self.log.debug("representations: {}".format(instance.data["representations"])) - # # Clean up - # for node in temporary_nodes: - # nuke.delete(node) + # Clean up + for node in temporary_nodes: + nuke.delete(node) def get_view_process_node(self): From 7f171bd11d16a806987f2a355808f2d1a59aa3c2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 14:49:45 +0100 Subject: [PATCH 17/30] feat(global): hook the conditions to preset tags after all that is what they are for ;) --- pype/plugins/global/publish/extract_review.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 418ac39186..a002e1140d 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -165,7 +165,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # scaling none square pixels and 1920 width # scale=320:-2 # to auto count height with output to be multiple of 2 - if profile.get('reformat', False): + if "reformat" in tags: pixel_aspect = instance.data["pixelAspect"] scaling_arg = "scale=1920:'ceil((1920/{})/2)*2':flags=lanczos,setsar=1".format( pixel_aspect) @@ -176,7 +176,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # baking lut file application lut_path = instance.data.get("lutPath") - if lut_path: + if lut_path and ("bake-lut" in tags): # removing Gama info as it is all baked in lut gamma = next((g for g in input_args if "-gamma" in g), None) From b69e9629a74e462c4be36aedc42152ece9a9c0f5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 14:50:08 +0100 Subject: [PATCH 18/30] clean(nuke): make it nicer --- pype/plugins/nuke/publish/extract_thumbnail.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/plugins/nuke/publish/extract_thumbnail.py b/pype/plugins/nuke/publish/extract_thumbnail.py index 126203603e..a58dad02f5 100644 --- a/pype/plugins/nuke/publish/extract_thumbnail.py +++ b/pype/plugins/nuke/publish/extract_thumbnail.py @@ -80,7 +80,7 @@ class ExtractThumbnail(pype.api.Extractor): 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)) + self.log.debug("k, v: {0}:{1}".format(k, v)) if isinstance(v, unicode): v = str(v) reformat_node[k].setValue(v) @@ -126,7 +126,8 @@ class ExtractThumbnail(pype.api.Extractor): # Render frames nuke.execute(write_node.name(), int(first_frame), int(last_frame)) - self.log.debug("representations: {}".format(instance.data["representations"])) + self.log.debug( + "representations: {}".format(instance.data["representations"])) # Clean up for node in temporary_nodes: From 305bd6a02181744668e0360f25ba70fb801b0f27 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 16:10:23 +0100 Subject: [PATCH 19/30] fix(global): not using correct preset tags fixing pixelAspect to by applied to letter box too --- pype/plugins/global/publish/extract_review.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index a002e1140d..9de4a966f3 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -31,7 +31,7 @@ class ExtractReview(pyblish.api.InstancePlugin): inst_data = instance.data fps = inst_data.get("fps") start_frame = inst_data.get("frameStart") - + pixel_aspect = instance.data["pixelAspect"] self.log.debug("Families In: `{}`".format(instance.data["families"])) # get representation and loop them @@ -147,13 +147,16 @@ class ExtractReview(pyblish.api.InstancePlugin): ) output_args = [] - output_args.extend(profile.get('codec', [])) + codec_args = profile.get('codec', []) + output_args.extend(codec_args) # preset's output data output_args.extend(profile.get('output', [])) # letter_box lb = profile.get('letter_box', None) if lb: + if "reformat" not in p_tags: + lb /= pixel_aspect output_args.append( "-filter:v drawbox=0:0:iw:round((ih-(iw*(1/{0})))/2):t=fill:c=black,drawbox=0:ih-round((ih-(iw*(1/{0})))/2):iw:round((ih-(iw*(1/{0})))/2):t=fill:c=black".format(lb)) @@ -165,8 +168,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # scaling none square pixels and 1920 width # scale=320:-2 # to auto count height with output to be multiple of 2 - if "reformat" in tags: - pixel_aspect = instance.data["pixelAspect"] + if "reformat" in p_tags: scaling_arg = "scale=1920:'ceil((1920/{})/2)*2':flags=lanczos,setsar=1".format( pixel_aspect) vf_back = self.add_video_filter_args( @@ -176,7 +178,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # baking lut file application lut_path = instance.data.get("lutPath") - if lut_path and ("bake-lut" in tags): + if lut_path and ("bake-lut" in p_tags): # removing Gama info as it is all baked in lut gamma = next((g for g in input_args if "-gamma" in g), None) @@ -220,7 +222,7 @@ class ExtractReview(pyblish.api.InstancePlugin): 'files': repr_file, "tags": new_tags, "outputName": name, - "codec": profile.get('codec', []) + "codec": codec_args }) if repre_new.get('preview'): repre_new.pop("preview") From 9727a70722f11a41394f1dbdbedda6245623f23d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 19:00:42 +0100 Subject: [PATCH 20/30] feat(global): making collection filesequences for nuke --- .../global/publish/collect_filesequences.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/collect_filesequences.py b/pype/plugins/global/publish/collect_filesequences.py index 39481e216b..d0ff5722a3 100644 --- a/pype/plugins/global/publish/collect_filesequences.py +++ b/pype/plugins/global/publish/collect_filesequences.py @@ -100,6 +100,8 @@ class CollectRenderedFrames(pyblish.api.ContextPlugin): label = "RenderedFrames" def process(self, context): + pixel_aspect = 1 + lut_path = None if os.environ.get("PYPE_PUBLISH_PATHS"): paths = os.environ["PYPE_PUBLISH_PATHS"].split(os.pathsep) self.log.info("Collecting paths: {}".format(paths)) @@ -144,6 +146,12 @@ class CollectRenderedFrames(pyblish.api.ContextPlugin): self.log.info("setting session using metadata") api.Session.update(session) os.environ.update(session) + instance = metadata.get("instance") + if instance: + instance_family = instance.get("family") + pixel_aspect = instance.get("pixelAspect", 1) + lut_path = instance.get("lutPath", None) + else: # Search in directory @@ -181,6 +189,8 @@ class CollectRenderedFrames(pyblish.api.ContextPlugin): families.append("ftrack") if "review" not in families: families.append("review") + if "write" in instance_family: + families.append("write") for collection in collections: instance = context.create_instance(str(collection)) @@ -197,6 +207,11 @@ class CollectRenderedFrames(pyblish.api.ContextPlugin): start = data.get("frameStart", indices[0]) end = data.get("frameEnd", indices[-1]) + self.log.debug("Collected pixel_aspect:\n" + "{}".format(pixel_aspect)) + self.log.debug("type pixel_aspect:\n" + "{}".format(type(pixel_aspect))) + # root = os.path.normpath(root) # self.log.info("Source: {}}".format(data.get("source", ""))) @@ -212,8 +227,11 @@ class CollectRenderedFrames(pyblish.api.ContextPlugin): "frameStart": start, "frameEnd": end, "fps": fps, - "source": data.get('source', '') + "source": data.get('source', ''), + "pixelAspect": pixel_aspect, }) + if lut_path: + instance.data.update({"lutPath": lut_path}) instance.append(collection) instance.context.data['fps'] = fps From 83f506d3eb94f830eea6a79be379a4f0d958d449 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 19:01:15 +0100 Subject: [PATCH 21/30] feat(nuke): removing families which are not needed anymore --- .../nuke/publish/extract_review_data_lut.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/pype/plugins/nuke/publish/extract_review_data_lut.py b/pype/plugins/nuke/publish/extract_review_data_lut.py index 910b6ee19e..dfc10952cd 100644 --- a/pype/plugins/nuke/publish/extract_review_data_lut.py +++ b/pype/plugins/nuke/publish/extract_review_data_lut.py @@ -20,23 +20,23 @@ class ExtractReviewLutData(pype.api.Extractor): hosts = ["nuke"] def process(self, instance): - self.log.debug( - "_ representations: {}".format(instance.data["representations"])) - + families = instance.data["families"] self.log.info("Creating staging dir...") - - stagingDir = instance.data[ - 'representations'][0]["stagingDir"].replace("\\", "/") - instance.data["stagingDir"] = stagingDir - - instance.data['representations'][0]["tags"] = ["review"] + if "representations" in instance.data: + staging_dir = instance.data[ + "representations"][0]["stagingDir"].replace("\\", "/") + instance.data["stagingDir"] = staging_dir + instance.data["representations"][0]["tags"] = ["review"] + 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"])) - if "representations" not in instance.data: - instance.data["representations"] = [] - with anlib.maintained_selection(): exporter = pnlib.Exporter_review_lut( self, instance @@ -48,6 +48,10 @@ class ExtractReviewLutData(pype.api.Extractor): exporter.stagingDir, exporter.file).replace("\\", "/") instance.data["representations"] += data["representations"] + if "render.farm" in families: + instance.data["families"].remove("review") + instance.data["families"].remove("ftrack") + self.log.debug( "_ lutPath: {}".format(instance.data["lutPath"])) self.log.debug( From 75145a4095b1ecaaa593488e6f82ecb1374cad19 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 19:01:43 +0100 Subject: [PATCH 22/30] feat(nuke): make thumbnail exporter available for render farm --- .../plugins/nuke/publish/extract_thumbnail.py | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/pype/plugins/nuke/publish/extract_thumbnail.py b/pype/plugins/nuke/publish/extract_thumbnail.py index a58dad02f5..3886fda569 100644 --- a/pype/plugins/nuke/publish/extract_thumbnail.py +++ b/pype/plugins/nuke/publish/extract_thumbnail.py @@ -15,7 +15,7 @@ class ExtractThumbnail(pype.api.Extractor): order = pyblish.api.ExtractorOrder + 0.01 label = "Extract Thumbnail" - families = ["review"] + families = ["review", "render.farm"] hosts = ["nuke"] def process(self, instance): @@ -28,14 +28,24 @@ class ExtractThumbnail(pype.api.Extractor): self.render_thumbnail(instance) def render_thumbnail(self, instance): - assert instance.data['representations'][0]['files'], "Instance data files should't be empty!" + node = instance[0] # group node + self.log.info("Creating staging dir...") + if "representations" not in instance.data: + staging_dir = instance.data[ + "representations"][0]["stagingDir"].replace("\\", "/") + instance.data["stagingDir"] = staging_dir + instance.data["representations"][0]["tags"] = ["review"] + 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"])) temporary_nodes = [] - self.log.info("Getting staging dir...") - stagingDir = instance.data[ - 'representations'][0]["stagingDir"].replace("\\", "/") - self.log.debug("StagingDir `{0}`...".format(stagingDir)) - collection = instance.data.get("collection", None) if collection: @@ -56,17 +66,21 @@ class ExtractThumbnail(pype.api.Extractor): if "#" in fhead: fhead = fhead.replace("#", "")[:-1] - rnode = nuke.createNode("Read") + path_render = os.path.join(staging_dir, fname).replace("\\", "/") + # check if file exist otherwise connect to write node + if os.path.isfile(path_render): + rnode = nuke.createNode("Read") - rnode["file"].setValue( - os.path.join(stagingDir, fname).replace("\\", "/")) + rnode["file"].setValue(path_render) - 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 + 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 + else: + previous_node = node # get input process and connect it to baking ipn = self.get_view_process_node() @@ -98,7 +112,7 @@ class ExtractThumbnail(pype.api.Extractor): write_node = nuke.createNode("Write") file = fhead + "jpeg" name = "thumbnail" - path = os.path.join(stagingDir, file).replace("\\", "/") + path = os.path.join(staging_dir, file).replace("\\", "/") instance.data["thumbnail"] = path write_node["file"].setValue(path) write_node["file_type"].setValue("jpeg") @@ -115,7 +129,7 @@ class ExtractThumbnail(pype.api.Extractor): 'name': name, 'ext': "jpeg", 'files': file, - "stagingDir": stagingDir, + "stagingDir": staging_dir, "frameStart": first_frame, "frameEnd": last_frame, "anatomy_template": "render", From 532e485e5d6588db1d4a0bfa9464da21fdc09cbc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 6 Dec 2019 19:01:59 +0100 Subject: [PATCH 23/30] feat(nuke): fixing deadline submiter --- .../nuke/publish/submit_nuke_deadline.py | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/pype/plugins/nuke/publish/submit_nuke_deadline.py b/pype/plugins/nuke/publish/submit_nuke_deadline.py index 4044026b5e..d9207d2bfc 100644 --- a/pype/plugins/nuke/publish/submit_nuke_deadline.py +++ b/pype/plugins/nuke/publish/submit_nuke_deadline.py @@ -1,9 +1,7 @@ import os import json import getpass - -import nuke - + from avalon import api from avalon.vendor import requests import re @@ -27,40 +25,36 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): def process(self, instance): - node = None - for x in instance: - if x.Class() == "Write": - node = x - - if node is None: - return + node = instance[0] + # for x in instance: + # if x.Class() == "Write": + # node = x + # + # if node is None: + # return DEADLINE_REST_URL = os.environ.get("DEADLINE_REST_URL", "http://localhost:8082") assert DEADLINE_REST_URL, "Requires DEADLINE_REST_URL" context = instance.context - workspace = os.path.dirname(context.data["currentFile"]) - filepath = None - # get path - path = nuke.filename(node) - output_dir = instance.data['outputDir'] + # get output path + render_path = instance.data['path'] + render_dir = os.path.normpath(os.path.dirname(render_path)) - filepath = context.data["currentFile"] + script_path = context.data["currentFile"] - self.log.debug(filepath) - - filename = os.path.basename(filepath) + script_name = os.path.basename(script_path) comment = context.data.get("comment", "") - dirname = os.path.join(workspace, "renders") + deadline_user = context.data.get("deadlineUser", getpass.getuser()) - jobname = "%s - %s" % (filename, instance.name) + jobname = "%s - %s" % (script_name, instance.name) ver = re.search(r"\d+\.\d+", context.data.get("hostVersion")) try: # Ensure render folder exists - os.makedirs(dirname) + os.makedirs(render_dir) except OSError: pass @@ -71,7 +65,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): payload = { "JobInfo": { # Top-level group name - "BatchName": filename, + "BatchName": script_name, # Job name, as seen in Monitor "Name": jobname, @@ -95,20 +89,20 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): }, "PluginInfo": { # Input - "SceneFile": filepath, + "SceneFile": script_path, # Output directory and filename - "OutputFilePath": dirname.replace("\\", "/"), + "OutputFilePath": render_dir.replace("\\", "/"), # "OutputFilePrefix": render_variables["filename_prefix"], # Mandatory for Deadline "Version": ver.group(), # Resolve relative references - "ProjectPath": workspace, - + "ProjectPath": script_path, + "AWSAssetFile0": render_path, # Only the specific write node is rendered. - "WriteNode": instance[0].name() + "WriteNode": node.name() }, # Mandatory for Deadline, may be empty From ac875a9306257eb078f5f2d971ce53dbd1f1f834 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 6 Dec 2019 22:44:50 +0100 Subject: [PATCH 24/30] use automatic preset loading --- pype/plugins/global/publish/extract_review.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 9de4a966f3..96c01fffb2 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -22,16 +22,17 @@ class ExtractReview(pyblish.api.InstancePlugin): families = ["review"] hosts = ["nuke", "maya", "shell"] + outputs = {} + ext_filter = [] + def process(self, instance): - # adding plugin attributes from presets - publish_presets = config.get_presets()["plugins"]["global"]["publish"] - plugin_attrs = publish_presets[self.__class__.__name__] - output_profiles = plugin_attrs.get("outputs", {}) + + output_profiles = self.outputs or {} inst_data = instance.data fps = inst_data.get("fps") start_frame = inst_data.get("frameStart") - pixel_aspect = instance.data["pixelAspect"] + pixel_aspect = instance.data.get("pixelAspect") or 1 self.log.debug("Families In: `{}`".format(instance.data["families"])) # get representation and loop them @@ -40,7 +41,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # filter out mov and img sequences representations_new = representations[:] for repre in representations: - if repre['ext'] in plugin_attrs["ext_filter"]: + if repre['ext'] in self.ext_filter: tags = repre.get("tags", []) self.log.info("Try repre: {}".format(repre)) @@ -92,8 +93,9 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.info("p_tags: `{}`".format(p_tags)) # add families [instance.data["families"].append(t) - for t in p_tags - if t not in instance.data["families"]] + for t in p_tags + if t not in instance.data["families"]] + # add to [new_tags.append(t) for t in p_tags if t not in new_tags] @@ -199,7 +201,6 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.info("Added Lut to ffmpeg command") self.log.debug("_ output_args: `{}`".format(output_args)) - mov_args = [ os.path.join( os.environ.get( @@ -247,7 +248,6 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.debug("Families Out: `{}`".format(instance.data["families"])) - def add_video_filter_args(self, args, inserting_arg): """ Fixing video filter argumets to be one long string From 0f50ead66153922a856c1ebf1623d88db97464a9 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 6 Dec 2019 22:47:54 +0100 Subject: [PATCH 25/30] remove unnecesary import --- pype/plugins/global/publish/extract_review.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 96c01fffb2..7554c080a0 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -3,7 +3,6 @@ import os import pyblish.api import clique import pype.api -from pypeapp import config class ExtractReview(pyblish.api.InstancePlugin): From c069f36d954a93f5786503aa8e3efcb43e8e3df8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sat, 7 Dec 2019 18:06:54 +0100 Subject: [PATCH 26/30] fix(global): reformat was not counting with ratio of format --- pype/plugins/global/publish/extract_review.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 9de4a966f3..936ae74c6f 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -32,6 +32,8 @@ class ExtractReview(pyblish.api.InstancePlugin): fps = inst_data.get("fps") start_frame = inst_data.get("frameStart") pixel_aspect = instance.data["pixelAspect"] + resolution_width = instance.data["resolutionWidth"] + resolution_height = instance.data["resolutionHeight"] self.log.debug("Families In: `{}`".format(instance.data["families"])) # get representation and loop them @@ -167,10 +169,9 @@ class ExtractReview(pyblish.api.InstancePlugin): output_args.append(full_output_path) # scaling none square pixels and 1920 width - # scale=320:-2 # to auto count height with output to be multiple of 2 if "reformat" in p_tags: scaling_arg = "scale=1920:'ceil((1920/{})/2)*2':flags=lanczos,setsar=1".format( - pixel_aspect) + (lb/pixel_aspect * (resolution_width / resolution_height))) vf_back = self.add_video_filter_args( output_args, scaling_arg) # add it to output_args From 8bf0874a4ad9368a21dde16be9759c906afde9ef Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 9 Dec 2019 12:12:27 +0100 Subject: [PATCH 27/30] fix(global): LetterBox can be 0 rather then None --- pype/plugins/global/publish/extract_review.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 77b00d52ec..a3707f4e59 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -156,8 +156,8 @@ class ExtractReview(pyblish.api.InstancePlugin): output_args.extend(profile.get('output', [])) # letter_box - lb = profile.get('letter_box', None) - if lb: + lb = profile.get('letter_box', 0) + if lb is not 0: if "reformat" not in p_tags: lb /= pixel_aspect output_args.append( From 101c3bc190d2785e9d337b4f4e1d4b0b7945d000 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 9 Dec 2019 13:25:34 +0100 Subject: [PATCH 28/30] fix(nuke): backward compatibility if avalon knob `ak:` --- pype/plugins/nuke/publish/collect_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index fbff28b282..3f3042ec46 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -34,7 +34,7 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): # get data from avalon knob self.log.debug("node[name]: {}".format(node['name'].value())) - avalon_knob_data = get_avalon_knob_data(node) + avalon_knob_data = get_avalon_knob_data(node, ["avalon:", "ak:"]) self.log.debug("avalon_knob_data: {}".format(avalon_knob_data)) From b2e7c60c28cb4917c7aca4f36a35f50b46a5ffdf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 10 Dec 2019 02:02:57 +0100 Subject: [PATCH 29/30] fix(global): reformat now works on width and height --- pype/plugins/global/publish/extract_review.py | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index a3707f4e59..786df95fc1 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -1,5 +1,5 @@ import os - +import math import pyblish.api import clique import pype.api @@ -31,8 +31,8 @@ class ExtractReview(pyblish.api.InstancePlugin): inst_data = instance.data fps = inst_data.get("fps") start_frame = inst_data.get("frameStart") - resolution_height = instance.dataget("resolutionHeight", 1080) - resolution_width = instance.dataget("resolutionWidth", 1920) + resolution_height = instance.data.get("resolutionHeight", 1080) + resolution_width = instance.data.get("resolutionWidth", 1920) pixel_aspect = instance.data.get("pixelAspect", 1) self.log.debug("Families In: `{}`".format(instance.data["families"])) @@ -169,10 +169,36 @@ class ExtractReview(pyblish.api.InstancePlugin): # output filename output_args.append(full_output_path) + self.log.debug("__ pixel_aspect: `{}`".format(pixel_aspect)) + self.log.debug("__ resolution_width: `{}`".format(resolution_width)) + self.log.debug("__ resolution_height: `{}`".format(resolution_height)) # scaling none square pixels and 1920 width if "reformat" in p_tags: - scaling_arg = "scale=1920:'ceil((1920/{})/2)*2':flags=lanczos,setsar=1".format( - (lb/pixel_aspect * (resolution_width / resolution_height))) + width_scale = 1920 + width_half_pad = 0 + res_w = int(float(resolution_width) * pixel_aspect) + height_half_pad = int(( + (res_w - 1920) / ( + res_w * .01) * ( + 1080 * .01)) / 2 + ) + height_scale = 1080 - (height_half_pad * 2) + if height_scale > 1080: + height_half_pad = 0 + height_scale = 1080 + width_half_pad = (1920 - (float(resolution_width) * (1080 / float(resolution_height))) ) / 2 + width_scale = int(1920 - (width_half_pad * 2)) + + self.log.debug("__ width_scale: `{}`".format(width_scale)) + self.log.debug("__ width_half_pad: `{}`".format(width_half_pad)) + self.log.debug("__ height_scale: `{}`".format(height_scale)) + self.log.debug("__ height_half_pad: `{}`".format(height_half_pad)) + + + scaling_arg = "scale={0}x{1}:flags=lanczos,pad=1920:1080:{2}:{3}:black,setsar=1".format( + width_scale, height_scale, width_half_pad, height_half_pad + ) + vf_back = self.add_video_filter_args( output_args, scaling_arg) # add it to output_args From 3faef65e44338e9380a208d8b1eabbf6c2d38afd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 10 Dec 2019 02:04:05 +0100 Subject: [PATCH 30/30] fix(nuke): little bits --- pype/plugins/global/publish/integrate_new.py | 2 +- pype/plugins/nuke/publish/collect_writes.py | 3 ++- pype/plugins/nuke/publish/extract_thumbnail.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 9021a3f997..c723631679 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -414,7 +414,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): } if sequence_repre and repre.get("frameStart"): - representation['context']['frame'] = src_padding_exp % repre.get("frameStart") + representation['context']['frame'] = src_padding_exp % int(repre.get("frameStart")) self.log.debug("__ representation: {}".format(representation)) destination_list.append(dst) diff --git a/pype/plugins/nuke/publish/collect_writes.py b/pype/plugins/nuke/publish/collect_writes.py index 5484d971bf..c9c516c888 100644 --- a/pype/plugins/nuke/publish/collect_writes.py +++ b/pype/plugins/nuke/publish/collect_writes.py @@ -76,7 +76,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): } try: - collected_frames = os.listdir(output_dir) + collected_frames = [f for f in os.listdir(output_dir) + if ext in f] if collected_frames: representation['frameStart'] = "%0{}d".format( len(str(last_frame))) % first_frame diff --git a/pype/plugins/nuke/publish/extract_thumbnail.py b/pype/plugins/nuke/publish/extract_thumbnail.py index 3886fda569..450bb39928 100644 --- a/pype/plugins/nuke/publish/extract_thumbnail.py +++ b/pype/plugins/nuke/publish/extract_thumbnail.py @@ -30,7 +30,7 @@ class ExtractThumbnail(pype.api.Extractor): def render_thumbnail(self, instance): node = instance[0] # group node self.log.info("Creating staging dir...") - if "representations" not in instance.data: + if "representations" in instance.data: staging_dir = instance.data[ "representations"][0]["stagingDir"].replace("\\", "/") instance.data["stagingDir"] = staging_dir