mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #3245 from pypeclub/feature/OP-3207_Nuke-multiple-baking-streams-with-correct-slate
This commit is contained in:
commit
6cb50ad693
8 changed files with 345 additions and 197 deletions
|
|
@ -26,7 +26,11 @@ from .pipeline import (
|
|||
update_container,
|
||||
)
|
||||
from .lib import (
|
||||
maintained_selection
|
||||
maintained_selection,
|
||||
reset_selection,
|
||||
get_view_process_node,
|
||||
duplicate_node
|
||||
|
||||
)
|
||||
|
||||
from .utils import (
|
||||
|
|
@ -58,6 +62,9 @@ __all__ = (
|
|||
"update_container",
|
||||
|
||||
"maintained_selection",
|
||||
"reset_selection",
|
||||
"get_view_process_node",
|
||||
"duplicate_node",
|
||||
|
||||
"colorspace_exists_on_node",
|
||||
"get_colorspace_list"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from pprint import pformat
|
|||
import re
|
||||
import six
|
||||
import platform
|
||||
import tempfile
|
||||
import contextlib
|
||||
from collections import OrderedDict
|
||||
|
||||
|
|
@ -711,6 +712,20 @@ def get_imageio_input_colorspace(filename):
|
|||
return preset_clrsp
|
||||
|
||||
|
||||
def get_view_process_node():
|
||||
reset_selection()
|
||||
|
||||
ipn_orig = None
|
||||
for v in nuke.allNodes(filter="Viewer"):
|
||||
ipn = v['input_process_node'].getValue()
|
||||
if "VIEWER_INPUT" not in ipn:
|
||||
ipn_orig = nuke.toNode(ipn)
|
||||
ipn_orig.setSelected(True)
|
||||
|
||||
if ipn_orig:
|
||||
return duplicate_node(ipn_orig)
|
||||
|
||||
|
||||
def on_script_load():
|
||||
''' Callback for ffmpeg support
|
||||
'''
|
||||
|
|
@ -2374,6 +2389,8 @@ def process_workfile_builder():
|
|||
env_value_to_bool,
|
||||
get_custom_workfile_template
|
||||
)
|
||||
# to avoid looping of the callback, remove it!
|
||||
nuke.removeOnCreate(process_workfile_builder, nodeClass="Root")
|
||||
|
||||
# get state from settings
|
||||
workfile_builder = get_current_project_settings()["nuke"].get(
|
||||
|
|
@ -2429,9 +2446,6 @@ def process_workfile_builder():
|
|||
if not openlv_on or not os.path.exists(last_workfile_path):
|
||||
return
|
||||
|
||||
# to avoid looping of the callback, remove it!
|
||||
nuke.removeOnCreate(process_workfile_builder, nodeClass="Root")
|
||||
|
||||
log.info("Opening last workfile...")
|
||||
# open workfile
|
||||
open_file(last_workfile_path)
|
||||
|
|
@ -2617,6 +2631,57 @@ class DirmapCache:
|
|||
return cls._sync_module
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _duplicate_node_temp():
|
||||
"""Create a temp file where node is pasted during duplication.
|
||||
|
||||
This is to avoid using clipboard for node duplication.
|
||||
"""
|
||||
|
||||
duplicate_node_temp_path = os.path.join(
|
||||
tempfile.gettempdir(),
|
||||
"openpype_nuke_duplicate_temp_{}".format(os.getpid())
|
||||
)
|
||||
|
||||
# This can happen only if 'duplicate_node' would be
|
||||
if os.path.exists(duplicate_node_temp_path):
|
||||
log.warning((
|
||||
"Temp file for node duplication already exists."
|
||||
" Trying to remove {}"
|
||||
).format(duplicate_node_temp_path))
|
||||
os.remove(duplicate_node_temp_path)
|
||||
|
||||
try:
|
||||
# Yield the path where node can be copied
|
||||
yield duplicate_node_temp_path
|
||||
|
||||
finally:
|
||||
# Remove the file at the end
|
||||
os.remove(duplicate_node_temp_path)
|
||||
|
||||
|
||||
def duplicate_node(node):
|
||||
reset_selection()
|
||||
|
||||
# select required node for duplication
|
||||
node.setSelected(True)
|
||||
|
||||
with _duplicate_node_temp() as filepath:
|
||||
# copy selected to temp filepath
|
||||
nuke.nodeCopy(filepath)
|
||||
|
||||
# reset selection
|
||||
reset_selection()
|
||||
|
||||
# paste node and selection is on it only
|
||||
dupli_node = nuke.nodePaste(filepath)
|
||||
|
||||
# reset selection
|
||||
reset_selection()
|
||||
|
||||
return dupli_node
|
||||
|
||||
|
||||
def dirmap_file_name_filter(file_name):
|
||||
"""Nuke callback function with single full path argument.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ from openpype.pipeline import (
|
|||
from .lib import (
|
||||
Knobby,
|
||||
check_subsetname_exists,
|
||||
reset_selection,
|
||||
maintained_selection,
|
||||
set_avalon_knob_data,
|
||||
add_publish_knob,
|
||||
get_nuke_imageio_settings,
|
||||
set_node_knobs_from_settings
|
||||
set_node_knobs_from_settings,
|
||||
get_view_process_node
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -216,37 +216,6 @@ class ExporterReview(object):
|
|||
|
||||
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
|
||||
"""
|
||||
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
|
||||
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()
|
||||
|
|
@ -311,7 +280,7 @@ class ExporterReviewLut(ExporterReview):
|
|||
self._temp_nodes = []
|
||||
self.log.info("Deleted nodes...")
|
||||
|
||||
def generate_lut(self):
|
||||
def generate_lut(self, **kwargs):
|
||||
bake_viewer_process = kwargs["bake_viewer_process"]
|
||||
bake_viewer_input_process_node = kwargs[
|
||||
"bake_viewer_input_process"]
|
||||
|
|
@ -329,7 +298,7 @@ class ExporterReviewLut(ExporterReview):
|
|||
if bake_viewer_process:
|
||||
# Node View Process
|
||||
if bake_viewer_input_process_node:
|
||||
ipn = self.get_view_input_process_node()
|
||||
ipn = get_view_process_node()
|
||||
if ipn is not None:
|
||||
# connect
|
||||
ipn.setInput(0, self.previous_node)
|
||||
|
|
@ -511,7 +480,7 @@ class ExporterReviewMov(ExporterReview):
|
|||
if bake_viewer_process:
|
||||
if bake_viewer_input_process_node:
|
||||
# View Process node
|
||||
ipn = self.get_view_input_process_node()
|
||||
ipn = get_view_process_node()
|
||||
if ipn is not None:
|
||||
# connect
|
||||
ipn.setInput(0, self.previous_node)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
from pprint import pformat
|
||||
import re
|
||||
import pyblish.api
|
||||
import openpype
|
||||
|
|
@ -50,6 +51,8 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
with maintained_selection():
|
||||
generated_repres = []
|
||||
for o_name, o_data in self.outputs.items():
|
||||
self.log.debug(
|
||||
"o_name: {}, o_data: {}".format(o_name, pformat(o_data)))
|
||||
f_families = o_data["filter"]["families"]
|
||||
f_task_types = o_data["filter"]["task_types"]
|
||||
f_subsets = o_data["filter"]["subsets"]
|
||||
|
|
@ -88,7 +91,13 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
# check if settings have more then one preset
|
||||
# so we dont need to add outputName to representation
|
||||
# in case there is only one preset
|
||||
multiple_presets = bool(len(self.outputs.keys()) > 1)
|
||||
multiple_presets = len(self.outputs.keys()) > 1
|
||||
|
||||
# adding bake presets to instance data for other plugins
|
||||
if not instance.data.get("bakePresets"):
|
||||
instance.data["bakePresets"] = {}
|
||||
# add preset to bakePresets
|
||||
instance.data["bakePresets"][o_name] = o_data
|
||||
|
||||
# create exporter instance
|
||||
exporter = plugin.ExporterReviewMov(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
import os
|
||||
from pprint import pformat
|
||||
import nuke
|
||||
import copy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
from openpype.hosts.nuke.api import (
|
||||
maintained_selection,
|
||||
duplicate_node,
|
||||
get_view_process_node
|
||||
)
|
||||
|
||||
|
||||
class ExtractSlateFrame(openpype.api.Extractor):
|
||||
|
|
@ -15,14 +20,13 @@ class ExtractSlateFrame(openpype.api.Extractor):
|
|||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder - 0.001
|
||||
order = pyblish.api.ExtractorOrder + 0.011
|
||||
label = "Extract Slate Frame"
|
||||
|
||||
families = ["slate"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
# Settings values
|
||||
# - can be extended by other attributes from node in the future
|
||||
key_value_mapping = {
|
||||
"f_submission_note": [True, "{comment}"],
|
||||
"f_submitting_for": [True, "{intent[value]}"],
|
||||
|
|
@ -30,44 +34,107 @@ class ExtractSlateFrame(openpype.api.Extractor):
|
|||
}
|
||||
|
||||
def process(self, instance):
|
||||
if hasattr(self, "viewer_lut_raw"):
|
||||
self.viewer_lut_raw = self.viewer_lut_raw
|
||||
else:
|
||||
self.viewer_lut_raw = False
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
self._create_staging_dir(instance)
|
||||
|
||||
with maintained_selection():
|
||||
self.log.debug("instance: {}".format(instance))
|
||||
self.log.debug("instance.data[families]: {}".format(
|
||||
instance.data["families"]))
|
||||
|
||||
self.render_slate(instance)
|
||||
if instance.data.get("bakePresets"):
|
||||
for o_name, o_data in instance.data["bakePresets"].items():
|
||||
self.log.info("_ o_name: {}, o_data: {}".format(
|
||||
o_name, pformat(o_data)))
|
||||
self.render_slate(
|
||||
instance,
|
||||
o_name,
|
||||
o_data["bake_viewer_process"],
|
||||
o_data["bake_viewer_input_process"]
|
||||
)
|
||||
else:
|
||||
# backward compatibility
|
||||
self.render_slate(instance)
|
||||
|
||||
# also render image to sequence
|
||||
self._render_slate_to_sequence(instance)
|
||||
|
||||
def _create_staging_dir(self, instance):
|
||||
|
||||
def render_slate(self, instance):
|
||||
node_subset_name = instance.data.get("name", None)
|
||||
node = instance[0] # group node
|
||||
self.log.info("Creating staging dir...")
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = list()
|
||||
|
||||
staging_dir = os.path.normpath(
|
||||
os.path.dirname(instance.data['path']))
|
||||
os.path.dirname(instance.data["path"]))
|
||||
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
|
||||
self.log.info(
|
||||
"StagingDir `{0}`...".format(instance.data["stagingDir"]))
|
||||
|
||||
frame_start = instance.data["frameStart"]
|
||||
frame_end = instance.data["frameEnd"]
|
||||
handle_start = instance.data["handleStart"]
|
||||
handle_end = instance.data["handleEnd"]
|
||||
def _check_frames_exists(self, instance):
|
||||
# rendering path from group write node
|
||||
fpath = instance.data["path"]
|
||||
|
||||
frame_length = int(
|
||||
(frame_end - frame_start + 1) + (handle_start + handle_end)
|
||||
)
|
||||
# instance frame range with handles
|
||||
first = instance.data["frameStartHandle"]
|
||||
last = instance.data["frameEndHandle"]
|
||||
|
||||
padding = fpath.count('#')
|
||||
|
||||
test_path_template = fpath
|
||||
if padding:
|
||||
repl_string = "#" * padding
|
||||
test_path_template = fpath.replace(
|
||||
repl_string, "%0{}d".format(padding))
|
||||
|
||||
for frame in range(first, last + 1):
|
||||
test_file = test_path_template % frame
|
||||
if not os.path.exists(test_file):
|
||||
self.log.debug("__ test_file: `{}`".format(test_file))
|
||||
return None
|
||||
|
||||
return True
|
||||
|
||||
def render_slate(
|
||||
self,
|
||||
instance,
|
||||
output_name=None,
|
||||
bake_viewer_process=True,
|
||||
bake_viewer_input_process=True
|
||||
):
|
||||
"""Slate frame renderer
|
||||
|
||||
Args:
|
||||
instance (PyblishInstance): Pyblish instance with subset data
|
||||
output_name (str, optional):
|
||||
Slate variation name. Defaults to None.
|
||||
bake_viewer_process (bool, optional):
|
||||
Switch for viewer profile baking. Defaults to True.
|
||||
bake_viewer_input_process (bool, optional):
|
||||
Switch for input process node baking. Defaults to True.
|
||||
"""
|
||||
slate_node = instance.data["slateNode"]
|
||||
|
||||
# rendering path from group write node
|
||||
fpath = instance.data["path"]
|
||||
|
||||
# instance frame range with handles
|
||||
first_frame = instance.data["frameStartHandle"]
|
||||
last_frame = instance.data["frameEndHandle"]
|
||||
|
||||
# fill slate node with comments
|
||||
self.add_comment_slate_node(instance, slate_node)
|
||||
|
||||
# solve output name if any is set
|
||||
_output_name = output_name or ""
|
||||
if _output_name:
|
||||
_output_name = "_" + _output_name
|
||||
|
||||
slate_first_frame = first_frame - 1
|
||||
|
||||
temporary_nodes = []
|
||||
collection = instance.data.get("collection", None)
|
||||
|
||||
if collection:
|
||||
|
|
@ -75,99 +142,101 @@ class ExtractSlateFrame(openpype.api.Extractor):
|
|||
fname = os.path.basename(collection.format(
|
||||
"{head}{padding}{tail}"))
|
||||
fhead = collection.format("{head}")
|
||||
|
||||
collected_frames_len = int(len(collection.indexes))
|
||||
|
||||
# get first and last frame
|
||||
first_frame = min(collection.indexes) - 1
|
||||
self.log.info('frame_length: {}'.format(frame_length))
|
||||
self.log.info(
|
||||
'len(collection.indexes): {}'.format(collected_frames_len)
|
||||
)
|
||||
if ("slate" in instance.data["families"]) \
|
||||
and (frame_length != collected_frames_len):
|
||||
first_frame += 1
|
||||
|
||||
last_frame = first_frame
|
||||
else:
|
||||
fname = os.path.basename(instance.data.get("path", None))
|
||||
fname = os.path.basename(fpath)
|
||||
fhead = os.path.splitext(fname)[0] + "."
|
||||
first_frame = instance.data.get("frameStartHandle", None) - 1
|
||||
last_frame = first_frame
|
||||
|
||||
if "#" in fhead:
|
||||
fhead = fhead.replace("#", "")[:-1]
|
||||
|
||||
previous_node = node
|
||||
self.log.debug("__ first_frame: {}".format(first_frame))
|
||||
self.log.debug("__ slate_first_frame: {}".format(slate_first_frame))
|
||||
|
||||
# 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)
|
||||
# fallback if files does not exists
|
||||
if self._check_frames_exists(instance):
|
||||
# Read node
|
||||
r_node = nuke.createNode("Read")
|
||||
r_node["file"].setValue(fpath)
|
||||
r_node["first"].setValue(first_frame)
|
||||
r_node["origfirst"].setValue(first_frame)
|
||||
r_node["last"].setValue(last_frame)
|
||||
r_node["origlast"].setValue(last_frame)
|
||||
r_node["colorspace"].setValue(instance.data["colorspace"])
|
||||
previous_node = r_node
|
||||
temporary_nodes = [previous_node]
|
||||
else:
|
||||
previous_node = slate_node.dependencies().pop()
|
||||
temporary_nodes = []
|
||||
|
||||
if not self.viewer_lut_raw:
|
||||
# only create colorspace baking if toggled on
|
||||
if bake_viewer_process:
|
||||
if bake_viewer_input_process:
|
||||
# get input process and connect it to baking
|
||||
ipn = get_view_process_node()
|
||||
if ipn is not None:
|
||||
ipn.setInput(0, previous_node)
|
||||
previous_node = ipn
|
||||
temporary_nodes.append(ipn)
|
||||
|
||||
# add duplicate slate node and connect to previous
|
||||
duply_slate_node = duplicate_node(slate_node)
|
||||
duply_slate_node.setInput(0, previous_node)
|
||||
previous_node = duply_slate_node
|
||||
temporary_nodes.append(duply_slate_node)
|
||||
|
||||
# add viewer display transformation node
|
||||
dag_node = nuke.createNode("OCIODisplay")
|
||||
dag_node.setInput(0, previous_node)
|
||||
previous_node = dag_node
|
||||
temporary_nodes.append(dag_node)
|
||||
|
||||
else:
|
||||
# add duplicate slate node and connect to previous
|
||||
duply_slate_node = duplicate_node(slate_node)
|
||||
duply_slate_node.setInput(0, previous_node)
|
||||
previous_node = duply_slate_node
|
||||
temporary_nodes.append(duply_slate_node)
|
||||
|
||||
# create write node
|
||||
write_node = nuke.createNode("Write")
|
||||
file = fhead + "slate.png"
|
||||
path = os.path.join(staging_dir, file).replace("\\", "/")
|
||||
instance.data["slateFrame"] = path
|
||||
file = fhead[:-1] + _output_name + "_slate.png"
|
||||
path = os.path.join(
|
||||
instance.data["stagingDir"], file).replace("\\", "/")
|
||||
|
||||
# add slate path to `slateFrames` instance data attr
|
||||
if not instance.data.get("slateFrames"):
|
||||
instance.data["slateFrames"] = {}
|
||||
|
||||
instance.data["slateFrames"][output_name or "*"] = path
|
||||
|
||||
# create write node
|
||||
write_node["file"].setValue(path)
|
||||
write_node["file_type"].setValue("png")
|
||||
write_node["raw"].setValue(1)
|
||||
write_node.setInput(0, previous_node)
|
||||
temporary_nodes.append(write_node)
|
||||
|
||||
# fill slate node with comments
|
||||
self.add_comment_slate_node(instance)
|
||||
|
||||
# Render frames
|
||||
nuke.execute(write_node.name(), int(first_frame), int(last_frame))
|
||||
# also render slate as sequence frame
|
||||
nuke.execute(node_subset_name, int(first_frame), int(last_frame))
|
||||
|
||||
self.log.debug(
|
||||
"slate frame path: {}".format(instance.data["slateFrame"]))
|
||||
nuke.execute(
|
||||
write_node.name(), int(slate_first_frame), int(slate_first_frame))
|
||||
|
||||
# Clean up
|
||||
for node in temporary_nodes:
|
||||
nuke.delete(node)
|
||||
|
||||
def get_view_process_node(self):
|
||||
# Select only the target node
|
||||
if nuke.selectedNodes():
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()]
|
||||
def _render_slate_to_sequence(self, instance):
|
||||
# set slate frame
|
||||
first_frame = instance.data["frameStartHandle"]
|
||||
slate_first_frame = first_frame - 1
|
||||
|
||||
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)
|
||||
# render slate as sequence frame
|
||||
nuke.execute(
|
||||
instance.data["name"],
|
||||
int(slate_first_frame),
|
||||
int(slate_first_frame)
|
||||
)
|
||||
|
||||
if ipn_orig:
|
||||
nuke.nodeCopy('%clipboard%')
|
||||
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()] # Deselect all
|
||||
|
||||
nuke.nodePaste('%clipboard%')
|
||||
|
||||
ipn = nuke.selectedNode()
|
||||
|
||||
return ipn
|
||||
|
||||
def add_comment_slate_node(self, instance):
|
||||
node = instance.data.get("slateNode")
|
||||
if not node:
|
||||
return
|
||||
def add_comment_slate_node(self, instance, node):
|
||||
|
||||
comment = instance.context.data.get("comment")
|
||||
intent = instance.context.data.get("intent")
|
||||
|
|
@ -186,8 +255,8 @@ class ExtractSlateFrame(openpype.api.Extractor):
|
|||
"intent": intent
|
||||
})
|
||||
|
||||
for key, value in self.key_value_mapping.items():
|
||||
enabled, template = value
|
||||
for key, _values in self.key_value_mapping.items():
|
||||
enabled, template = _values
|
||||
if not enabled:
|
||||
self.log.debug("Key \"{}\" is disabled".format(key))
|
||||
continue
|
||||
|
|
@ -221,5 +290,5 @@ class ExtractSlateFrame(openpype.api.Extractor):
|
|||
))
|
||||
except NameError:
|
||||
self.log.warning((
|
||||
"Failed to set value \"{}\" on node attribute \"{}\""
|
||||
"Failed to set value \"{0}\" on node attribute \"{0}\""
|
||||
).format(value))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ import os
|
|||
import nuke
|
||||
import pyblish.api
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api.lib import maintained_selection
|
||||
from openpype.hosts.nuke.api import (
|
||||
maintained_selection,
|
||||
get_view_process_node
|
||||
)
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
|
|
@ -17,7 +20,7 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder + 0.01
|
||||
order = pyblish.api.ExtractorOrder + 0.011
|
||||
label = "Extract Thumbnail"
|
||||
|
||||
families = ["review"]
|
||||
|
|
@ -39,15 +42,32 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
self.log.debug("instance.data[families]: {}".format(
|
||||
instance.data["families"]))
|
||||
|
||||
self.render_thumbnail(instance)
|
||||
if instance.data.get("bakePresets"):
|
||||
for o_name, o_data in instance.data["bakePresets"].items():
|
||||
self.render_thumbnail(instance, o_name, **o_data)
|
||||
else:
|
||||
viewer_process_swithes = {
|
||||
"bake_viewer_process": True,
|
||||
"bake_viewer_input_process": True
|
||||
}
|
||||
self.render_thumbnail(instance, None, **viewer_process_swithes)
|
||||
|
||||
def render_thumbnail(self, instance):
|
||||
def render_thumbnail(self, instance, output_name=None, **kwargs):
|
||||
first_frame = instance.data["frameStartHandle"]
|
||||
last_frame = instance.data["frameEndHandle"]
|
||||
|
||||
# find frame range and define middle thumb frame
|
||||
mid_frame = int((last_frame - first_frame) / 2)
|
||||
|
||||
# solve output name if any is set
|
||||
output_name = output_name or ""
|
||||
if output_name:
|
||||
output_name = "_" + output_name
|
||||
|
||||
bake_viewer_process = kwargs["bake_viewer_process"]
|
||||
bake_viewer_input_process_node = kwargs[
|
||||
"bake_viewer_input_process"]
|
||||
|
||||
node = instance[0] # group node
|
||||
self.log.info("Creating staging dir...")
|
||||
|
||||
|
|
@ -106,17 +126,7 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
temporary_nodes.append(rnode)
|
||||
previous_node = rnode
|
||||
|
||||
# bake viewer input look node into thumbnail image
|
||||
if self.bake_viewer_input_process:
|
||||
# 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:
|
||||
|
|
@ -129,8 +139,16 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
previous_node = reformat_node
|
||||
temporary_nodes.append(reformat_node)
|
||||
|
||||
# bake viewer colorspace into thumbnail image
|
||||
if self.bake_viewer_process:
|
||||
# only create colorspace baking if toggled on
|
||||
if bake_viewer_process:
|
||||
if bake_viewer_input_process_node:
|
||||
# get input process and connect it to baking
|
||||
ipn = get_view_process_node()
|
||||
if ipn is not None:
|
||||
ipn.setInput(0, previous_node)
|
||||
previous_node = ipn
|
||||
temporary_nodes.append(ipn)
|
||||
|
||||
dag_node = nuke.createNode("OCIODisplay")
|
||||
dag_node.setInput(0, previous_node)
|
||||
previous_node = dag_node
|
||||
|
|
@ -138,7 +156,7 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
|
||||
# create write node
|
||||
write_node = nuke.createNode("Write")
|
||||
file = fhead + "jpg"
|
||||
file = fhead[:-1] + output_name + ".jpg"
|
||||
name = "thumbnail"
|
||||
path = os.path.join(staging_dir, file).replace("\\", "/")
|
||||
instance.data["thumbnail"] = path
|
||||
|
|
@ -168,30 +186,3 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
# Clean up
|
||||
for node in temporary_nodes:
|
||||
nuke.delete(node)
|
||||
|
||||
def get_view_process_node(self):
|
||||
|
||||
# Select only the target node
|
||||
if nuke.selectedNodes():
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()]
|
||||
|
||||
ipn_orig = None
|
||||
for v in [n for n in nuke.allNodes()
|
||||
if "Viewer" == n.Class()]:
|
||||
ip = v['input_process'].getValue()
|
||||
ipn = v['input_process_node'].getValue()
|
||||
if "VIEWER_INPUT" not in ipn and ip:
|
||||
ipn_orig = nuke.toNode(ipn)
|
||||
ipn_orig.setSelected(True)
|
||||
|
||||
if ipn_orig:
|
||||
nuke.nodeCopy('%clipboard%')
|
||||
|
||||
# Deselect all
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()]
|
||||
|
||||
nuke.nodePaste('%clipboard%')
|
||||
|
||||
ipn = nuke.selectedNode()
|
||||
|
||||
return ipn
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
|||
# mapping of instance properties to be transfered to new instance for every
|
||||
# specified family
|
||||
instance_transfer = {
|
||||
"slate": ["slateFrame"],
|
||||
"slate": ["slateFrames"],
|
||||
"review": ["lutPath"],
|
||||
"render2d": ["bakingNukeScripts", "version"],
|
||||
"renderlayer": ["convertToScanline"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import os
|
||||
from pprint import pformat
|
||||
import re
|
||||
import openpype.api
|
||||
import pyblish
|
||||
from openpype.lib import (
|
||||
|
|
@ -21,6 +23,8 @@ class ExtractReviewSlate(openpype.api.Extractor):
|
|||
families = ["slate", "review"]
|
||||
match = pyblish.api.Subset
|
||||
|
||||
SUFFIX = "_slate"
|
||||
|
||||
hosts = ["nuke", "shell"]
|
||||
optional = True
|
||||
|
||||
|
|
@ -29,28 +33,19 @@ class ExtractReviewSlate(openpype.api.Extractor):
|
|||
if "representations" not in inst_data:
|
||||
raise RuntimeError("Burnin needs already created mov to work on.")
|
||||
|
||||
suffix = "_slate"
|
||||
slate_path = inst_data.get("slateFrame")
|
||||
# get slates frame from upstream
|
||||
slates_data = inst_data.get("slateFrames")
|
||||
if not slates_data:
|
||||
# make it backward compatible and open for slates generator
|
||||
# premium plugin
|
||||
slates_data = {
|
||||
"*": inst_data["slateFrame"]
|
||||
}
|
||||
|
||||
self.log.info("_ slates_data: {}".format(pformat(slates_data)))
|
||||
|
||||
ffmpeg_path = get_ffmpeg_tool_path("ffmpeg")
|
||||
|
||||
slate_streams = get_ffprobe_streams(slate_path, self.log)
|
||||
# Try to find first stream with defined 'width' and 'height'
|
||||
# - this is to avoid order of streams where audio can be as first
|
||||
# - there may be a better way (checking `codec_type`?)+
|
||||
slate_width = None
|
||||
slate_height = None
|
||||
for slate_stream in slate_streams:
|
||||
if "width" in slate_stream and "height" in slate_stream:
|
||||
slate_width = int(slate_stream["width"])
|
||||
slate_height = int(slate_stream["height"])
|
||||
break
|
||||
|
||||
# Raise exception of any stream didn't define input resolution
|
||||
if slate_width is None:
|
||||
raise AssertionError((
|
||||
"FFprobe couldn't read resolution from input file: \"{}\""
|
||||
).format(slate_path))
|
||||
|
||||
if "reviewToWidth" in inst_data:
|
||||
use_legacy_code = True
|
||||
else:
|
||||
|
|
@ -77,6 +72,12 @@ class ExtractReviewSlate(openpype.api.Extractor):
|
|||
streams = get_ffprobe_streams(
|
||||
input_path, self.log
|
||||
)
|
||||
# get slate data
|
||||
slate_path = self._get_slate_path(input_file, slates_data)
|
||||
self.log.info("_ slate_path: {}".format(slate_path))
|
||||
|
||||
slate_width, slate_height = self._get_slates_resolution(slate_path)
|
||||
|
||||
# Get video metadata
|
||||
(
|
||||
input_width,
|
||||
|
|
@ -138,7 +139,7 @@ class ExtractReviewSlate(openpype.api.Extractor):
|
|||
_remove_at_end = []
|
||||
|
||||
ext = os.path.splitext(input_file)[1]
|
||||
output_file = input_file.replace(ext, "") + suffix + ext
|
||||
output_file = input_file.replace(ext, "") + self.SUFFIX + ext
|
||||
|
||||
_remove_at_end.append(input_path)
|
||||
|
||||
|
|
@ -369,6 +370,43 @@ class ExtractReviewSlate(openpype.api.Extractor):
|
|||
|
||||
self.log.debug(inst_data["representations"])
|
||||
|
||||
def _get_slate_path(self, input_file, slates_data):
|
||||
slate_path = None
|
||||
for sl_n, _slate_path in slates_data.items():
|
||||
if "*" in sl_n:
|
||||
slate_path = _slate_path
|
||||
break
|
||||
elif re.search(sl_n, input_file):
|
||||
slate_path = _slate_path
|
||||
break
|
||||
|
||||
if not slate_path:
|
||||
raise AttributeError(
|
||||
"Missing slates paths: {}".format(slates_data))
|
||||
|
||||
return slate_path
|
||||
|
||||
def _get_slates_resolution(self, slate_path):
|
||||
slate_streams = get_ffprobe_streams(slate_path, self.log)
|
||||
# Try to find first stream with defined 'width' and 'height'
|
||||
# - this is to avoid order of streams where audio can be as first
|
||||
# - there may be a better way (checking `codec_type`?)+
|
||||
slate_width = None
|
||||
slate_height = None
|
||||
for slate_stream in slate_streams:
|
||||
if "width" in slate_stream and "height" in slate_stream:
|
||||
slate_width = int(slate_stream["width"])
|
||||
slate_height = int(slate_stream["height"])
|
||||
break
|
||||
|
||||
# Raise exception of any stream didn't define input resolution
|
||||
if slate_width is None:
|
||||
raise AssertionError((
|
||||
"FFprobe couldn't read resolution from input file: \"{}\""
|
||||
).format(slate_path))
|
||||
|
||||
return (slate_width, slate_height)
|
||||
|
||||
def _get_video_metadata(self, streams):
|
||||
input_timecode = ""
|
||||
input_width = None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue