Merge pull request #2248 from pypeclub/feature/OP-1933_nuke-toggle-baking-colorspace

This commit is contained in:
Jakub Ježek 2021-11-25 11:55:43 +01:00 committed by GitHub
commit d82d400f1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 685 additions and 462 deletions

View file

@ -41,6 +41,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)
'''
@ -51,8 +55,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:
@ -70,8 +73,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:
@ -553,8 +555,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")
@ -912,8 +913,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"])
@ -1170,386 +1170,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.

View file

@ -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

View file

@ -1,3 +1,4 @@
import os
import random
import string
@ -100,3 +101,414 @@ class NukeLoader(api.Loader):
nuke.delete(member)
return dependent_nodes
class ExporterReview(object):
"""
Base class object for generating review data from Nuke
Args:
klass (pyblish.plugin): pyblish plugin parent
instance (pyblish.instance): instance of pyblish context
"""
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 = tags or []
repre = {
'outputName': self.name,
'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_input_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 get_imageio_baking_profile(self):
from . import lib as opnlib
nuke_imageio = opnlib.get_nuke_imageio_settings()
# 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):
"""
Generator object for review lut from Nuke
Args:
klass (pyblish.plugin): pyblish plugin parent
instance (pyblish.instance): instance of pyblish context
"""
_temp_nodes = []
def __init__(self,
klass,
instance,
name=None,
ext=None,
cube_size=None,
lut_size=None,
lut_style=None):
# initialize parent class
super(ExporterReviewLut, self).__init__(klass, instance)
# 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 clean_nodes(self):
for node in self._temp_nodes:
nuke.delete(node)
self._temp_nodes = []
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
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))
if bake_viewer_process:
# Node View Process
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)
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
"""
_temp_nodes = {}
def __init__(self,
klass,
instance,
name=None,
ext=None,
):
# initialize parent class
super(ExporterReviewMov, self).__init__(klass, instance)
# passing presets for nodes to self
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
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 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
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, **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
# 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[subset].append(r_node)
self.previous_node = r_node
self.log.debug("Read... `{}`".format(self._temp_nodes[subset]))
# only create colorspace baking if toggled on
if bake_viewer_process:
if bake_viewer_input_process_node:
# View Process node
ipn = self.get_view_input_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:
# OCIODisplay
dag_node = nuke.createNode("OCIODisplay")
dag_node["view"].setValue(str(baking_view_profile))
# connect
dag_node.setInput(0, self.previous_node)
self._temp_nodes[subset].append(dag_node)
self.previous_node = dag_node
self.log.debug("OCIODisplay... `{}`".format(
self._temp_nodes[subset]))
# Write node
write_node = nuke.createNode("Write")
self.log.debug("Path: {}".format(self.path))
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 should't this come from settings on outputs?
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")
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
write_node.setInput(0, self.previous_node)
self._temp_nodes[subset].append(write_node)
self.log.debug("Write... `{}`".format(self._temp_nodes[subset]))
# ---------- 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"] + add_tags,
range=True
)
self.log.debug("Representation... `{}`".format(self.data))
self.clean_nodes(subset)
nuke.scriptSave()
return self.data

View file

@ -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)"""

View file

@ -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

View file

@ -1,16 +1,9 @@
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:
from __builtin__ import reload
except ImportError:
from importlib import reload
reload(pnlib)
class ExtractReviewDataLut(openpype.api.Extractor):
"""Extracts movie and thumbnail with baked in luts
@ -45,7 +38,7 @@ class ExtractReviewDataLut(openpype.api.Extractor):
# generate data
with anlib.maintained_selection():
exporter = pnlib.ExporterReviewLut(
exporter = plugin.ExporterReviewLut(
self, instance
)
data = exporter.generate_lut()

View file

@ -1,16 +1,9 @@
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:
from __builtin__ import reload
except ImportError:
from importlib import reload
reload(pnlib)
class ExtractReviewDataMov(openpype.api.Extractor):
"""Extracts movie and thumbnail with baked in luts
@ -27,15 +20,15 @@ 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:
instance.data["representations"] = list()
instance.data["representations"] = []
staging_dir = os.path.normpath(
os.path.dirname(instance.data['path']))
@ -45,28 +38,80 @@ 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 = pnlib.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 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)
])
self.log.debug(
"_ data: {}".format(data))
# 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)
])
instance.data.update({
"bakeRenderPath": data.get("bakeRenderPath"),
"bakeScriptPath": data.get("bakeScriptPath"),
"bakeWriteNodeName": data.get("bakeWriteNodeName")
})
else:
data = exporter.generate_mov()
# we need all filters to be positive for this
# preset to be activated
test_all = all([
test_families,
test_task_types
])
# assign to representations
instance.data["representations"] += data["representations"]
# if it is not positive then skip this preset
if not test_all:
continue
self.log.info(
"Baking output `{}` with settings: {}".format(
o_name, o_data))
# 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(
"_ representations: {}".format(instance.data["representations"]))
"_ representations: {}".format(
instance.data["representations"]))

View file

@ -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:
_name, ext = os.path.splitext(file_url)
# 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

View file

@ -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:

View file

@ -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"]
}
@ -506,9 +505,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
@ -524,7 +523,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])
@ -596,7 +595,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"]

View file

@ -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,41 @@ 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)
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((
@ -283,6 +309,8 @@ 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))
# Delete input files
for filepath in files_to_delete:
if os.path.exists(filepath):

View file

@ -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,8 +195,20 @@ 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(
"New representation tags: `{}`".format(new_repre["tags"])
"Linked burnins: `{}`".format(new_repre.get("burnins"))
)
self.log.debug(
"New representation tags: `{}`".format(
new_repre.get("tags"))
)
temp_data = self.prepare_temp_data(
@ -232,12 +247,16 @@ 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", "")
output_ext = new_repre["ext"]
if output_name:
output_name += "_"
output_name += output_def["filename_suffix"]
if temp_data["without_handles"]:
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"],

View file

@ -28,6 +28,9 @@
"viewer": {
"viewerProcess": "sRGB"
},
"baking": {
"viewerProcess": "rec709"
},
"workfile": {
"colorManagement": "Nuke",
"OCIO_config": "nuke-default",

View file

@ -52,6 +52,7 @@
"burnin",
"ftrackreview"
],
"burnins": [],
"ffmpeg_args": {
"video_filters": [],
"audio_filters": [],

View file

@ -110,7 +110,20 @@
},
"ExtractReviewDataMov": {
"enabled": true,
"viewer_lut_raw": false
"viewer_lut_raw": false,
"outputs": {
"baking": {
"filter": {
"task_types": [],
"families": []
},
"extension": "mov",
"viewer_process_override": "",
"bake_viewer_process": true,
"bake_viewer_input_process": true,
"add_tags": []
}
}
},
"ExtractSlateFrame": {
"viewer_lut_raw": false

View file

@ -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",

View file

@ -212,6 +212,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",

View file

@ -167,7 +167,67 @@
"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": "extension",
"label": "File extension"
},
{
"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_tags",
"label": "Add additional tags to representations",
"type": "list",
"object_type": "text"
}
]
}
}
]
},
{

@ -1 +1 @@
Subproject commit 9499f6517a1ff2d3bf94c5d34c0aece146734760
Subproject commit 7e5efd6885330d84bb8495975bcab84df49bfa3d