Merged in feature/PYPE-81-nuke-write-render-workflow (pull request #235)

Feature/PYPE-81 nuke write render workflow

Approved-by: Milan Kolar <milan@orbi.tools>
This commit is contained in:
Jakub Jezek 2019-07-31 00:57:15 +00:00 committed by Milan Kolar
commit b6869a9379
10 changed files with 254 additions and 121 deletions

View file

@ -18,6 +18,7 @@ log = Logger().get_logger(__name__, "nuke")
self = sys.modules[__name__]
self._project = None
def onScriptLoad():
if nuke.env['LINUX']:
nuke.tcl('load ffmpegReader')
@ -43,7 +44,7 @@ def checkInventoryVersions():
container = avalon.nuke.parse_container(each)
if container:
node = container["_tool"]
node = container["_node"]
avalon_knob_data = get_avalon_knob_data(node)
# get representation from io
@ -102,6 +103,9 @@ def writes_version_sync():
node_new_file = node_file.replace(node_version, new_version)
each['file'].setValue(node_new_file)
if not os.path.isdir(os.path.dirname(node_new_file)):
log.info("path does not exist")
os.makedirs(os.path.dirname(node_new_file), 0o766)
except Exception as e:
log.debug(
"Write node: `{}` has no version in path: {}".format(each.name(), e))
@ -172,7 +176,32 @@ def script_name():
return nuke.root().knob('name').value()
def create_write_node(name, data):
def create_write_node(name, data, prenodes=None):
'''Creating write node which is group node
Arguments:
name (str): name of node
data (dict): data to be imprinted
prenodes (list, optional): list of lists, definitions for nodes
to be created before write
Example:
prenodes = [(
"NameNode", # string
"NodeClass", # string
( # OrderDict: knob and values pairs
("knobName", "knobValue"),
("knobName", "knobValue")
),
( # list inputs
"firstPrevNodeName",
"secondPrevNodeName"
)
)
]
'''
nuke_dataflow_writes = get_node_dataflow_preset(**data)
nuke_colorspace_writes = get_node_colorspace_preset(**data)
application = lib.get_application(os.environ["AVALON_APP_NAME"])
@ -191,16 +220,8 @@ def create_write_node(name, data):
# build file path to workfiles
fpath = str(anatomy_filled["work"]["folder"]).replace("\\", "/")
pattern = "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}"
# Workfile paths can be configured to have host name in file path.
# In this case we want to avoid duplicate folder names.
if "nuke" in fpath.lower():
pattern = pattern.replace("nuke/", "")
fpath = pattern.format(
work=fpath,
version=data["version"],
subset=data["subset"],
fpath = data["fpath_template"].format(
work=fpath, version=data["version"], subset=data["subset"],
frame=data["frame"],
ext=data["nuke_dataflow_writes"]["file_type"]
)
@ -228,14 +249,89 @@ def create_write_node(name, data):
log.debug(_data)
_data["frame_range"] = data.get("frame_range", None)
log.info("__ _data3: {}".format(_data))
instance = avalon.nuke.lib.add_write_node(
name,
**_data
)
instance = avalon.nuke.lib.imprint(instance, data["avalon"])
add_rendering_knobs(instance)
return instance
# todo: hange this to new way
GN = nuke.createNode("Group", "name {}".format(name))
prev_node = None
with GN:
# creating pre-write nodes `prenodes`
if prenodes:
for name, klass, properties, set_input_to in prenodes:
# create node
now_node = nuke.createNode(klass, "name {}".format(name))
# add data to knob
for k, v in properties:
if k and v:
now_node[k].serValue(str(v))
# connect to previous node
if set_input_to:
if isinstance(set_input_to, (tuple or list)):
for i, node_name in enumerate(set_input_to):
input_node = nuke.toNode(node_name)
now_node.setInput(1, input_node)
elif isinstance(set_input_to, str):
input_node = nuke.toNode(set_input_to)
now_node.setInput(0, input_node)
else:
now_node.setInput(0, prev_node)
# swith actual node to previous
prev_node = now_node
else:
prev_node = nuke.createNode("Input", "name rgba")
# creating write node
now_node = avalon.nuke.lib.add_write_node("inside_{}".format(name),
**_data
)
write_node = now_node
# connect to previous node
now_node.setInput(0, prev_node)
# swith actual node to previous
prev_node = now_node
now_node = nuke.createNode("Output", "name write")
# connect to previous node
now_node.setInput(0, prev_node)
# imprinting group node
GN = avalon.nuke.imprint(GN, data["avalon"])
divider = nuke.Text_Knob('')
GN.addKnob(divider)
add_rendering_knobs(GN)
divider = nuke.Text_Knob('')
GN.addKnob(divider)
# set tile color
tile_color = _data.get("tile_color", "0xff0000ff")
GN["tile_color"].setValue(tile_color)
# add render button
lnk = nuke.Link_Knob("Render")
lnk.makeLink(write_node.name(), "Render")
lnk.setName("Render")
GN.addKnob(lnk)
# linking knobs to group property panel
linking_knobs = ["first", "last", "use_limit"]
for k in linking_knobs:
lnk = nuke.Link_Knob(k)
lnk.makeLink(write_node.name(), k)
lnk.setName(k.replace('_', ' ').capitalize())
lnk.clearFlag(nuke.STARTLINE)
GN.addKnob(lnk)
return GN
def add_rendering_knobs(node):
@ -414,8 +510,8 @@ def reset_frame_range_handles():
# adding handle_start/end to root avalon knob
if not avalon.nuke.set_avalon_knob_data(root, {
"handle_start": handle_start,
"handle_end": handle_end
"handle_start": int(handle_start),
"handle_end": int(handle_end)
}):
log.warning("Cannot set Avalon knob to Root node!")
@ -436,34 +532,26 @@ def reset_resolution():
asset = api.Session["AVALON_ASSET"]
asset = io.find_one({"name": asset, "type": "asset"})
try:
width = asset.get('data', {}).get('resolution_width', 1920)
height = asset.get('data', {}).get('resolution_height', 1080)
pixel_aspect = asset.get('data', {}).get('pixel_aspect', 1)
bbox = asset.get('data', {}).get('crop', "0.0.1920.1080")
width = asset.get('data', {}).get('resolution_width')
height = asset.get('data', {}).get('resolution_height')
pixel_aspect = asset.get('data', {}).get('pixel_aspect')
if bbox not in "0.0.1920.1080":
try:
x, y, r, t = bbox.split(".")
except Exception as e:
x = 0
y = 0
r = width
t = height
bbox = None
log.error("{}: {} \nFormat:Crop need to be set with dots, example: "
"0.0.1920.1080, /nSetting to default".format(__name__, e))
else:
bbox = None
except KeyError:
log.warning(
"No resolution information found for \"{0}\".".format(
project["name"]
)
)
log.info("pixel_aspect: {}".format(pixel_aspect))
if any(not x for x in [width, height, pixel_aspect]):
log.error("Missing set shot attributes in DB. \nContact your supervisor!. \n\nWidth: `{0}` \nHeight: `{1}` \nPixel Asspect: `{2}`".format(
width, height, pixel_aspect))
return
bbox = asset.get('data', {}).get('crop')
if bbox:
try:
x, y, r, t = bbox.split(".")
except Exception as e:
bbox = None
log.error("{}: {} \nFormat:Crop need to be set with dots, example: "
"0.0.1920.1080, /nSetting to default".format(__name__, e))
used_formats = list()
for f in nuke.formats():
if project["name"] in str(f.name()):
@ -620,7 +708,7 @@ def get_hierarchical_attr(entity, attr, default=None):
# dict
# """
#
# node = container["_tool"]
# node = container["_node"]
# tile_color = node['tile_color'].value()
# if tile_color is None:
# return {}

View file

@ -0,0 +1,27 @@
import pyblish.api
import os
class IntegrateCleanComponentData(pyblish.api.InstancePlugin):
"""
Cleaning up thumbnail an mov files after they have been integrated
"""
order = pyblish.api.IntegratorOrder + 0.5
label = 'Clean component data'
families = ["ftrack"]
optional = True
active = True
def process(self, instance):
for comp in instance.data['representations']:
self.log.debug('component {}'.format(comp))
if comp.get('thumbnail') or ("thumbnail" in comp.get('tags', [])):
os.remove(comp['published_path'])
self.log.info('Thumbnail image was erased')
elif comp.get('preview') or ("preview" in comp.get('tags', [])):
os.remove(comp['published_path'])
self.log.info('Preview mov file was erased')

View file

@ -1,17 +0,0 @@
# type: render
# if no render type node in script then first is having in name [master] for definition of main script renderer
# colorspace setting from templates
# dataflow setting from templates
# type: mask_render
# created with shuffle gizmo for RGB separation into davinci matte
# colorspace setting from templates
# dataflow setting from templates
# type: prerender
# backdrop with write and read
# colorspace setting from templates
# dataflow setting from templates
# type: geo
# dataflow setting from templates

View file

@ -3,6 +3,7 @@ import avalon.api
import avalon.nuke
from pype.nuke.lib import create_write_node
from pype import api as pype
from pypeapp import config
import nuke
@ -30,6 +31,11 @@ class CreateWriteRender(avalon.nuke.Creator):
def __init__(self, *args, **kwargs):
super(CreateWriteRender, self).__init__(*args, **kwargs)
self.presets = config.get_presets()['plugins']["nuke"]["create"].get(
self.__class__.__name__, {}
)
self.name = self.data["subset"]
data = OrderedDict()
@ -41,7 +47,6 @@ class CreateWriteRender(avalon.nuke.Creator):
self.data = data
def process(self):
self.name = self.data["subset"]
family = self.family
node = 'write'
@ -55,6 +60,16 @@ class CreateWriteRender(avalon.nuke.Creator):
"avalon": self.data
}
if self.presets.get('fpath_template'):
self.log.info("Adding template path from preset")
write_data.update(
{"fpath_template": self.presets["fpath_template"]}
)
else:
self.log.info("Adding template path from plugin")
write_data.update({
"fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}"})
create_write_node(self.data["subset"], write_data)
return
@ -74,6 +89,9 @@ class CreateWritePrerender(avalon.nuke.Creator):
def __init__(self, *args, **kwargs):
super(CreateWritePrerender, self).__init__(*args, **kwargs)
self.presets = config.get_presets()['plugins']["nuke"]["create"].get(
self.__class__.__name__, {}
)
data = OrderedDict()
@ -97,56 +115,16 @@ class CreateWritePrerender(avalon.nuke.Creator):
"avalon": self.data
}
if self.presets.get('fpath_template'):
self.log.info("Adding template path from preset")
write_data.update(
{"fpath_template": self.presets["fpath_template"]}
)
else:
self.log.info("Adding template path from plugin")
write_data.update({
"fpath_template": "{work}/prerenders/{subset}/{subset}.{frame}.{ext}"})
create_write_node(self.data["subset"], write_data)
return
"""
class CrateWriteStill(avalon.nuke.Creator):
# change this to template preset
preset = "still"
name = "WriteStill"
label = "Create Write Still"
hosts = ["nuke"]
family = "{}_write".format(preset)
families = preset
icon = "image"
def __init__(self, *args, **kwargs):
super(CrateWriteStill, self).__init__(*args, **kwargs)
data = OrderedDict()
data["family"] = self.family.split("_")[-1]
data["families"] = self.families
{data.update({k: v}) for k, v in self.data.items()
if k not in data.keys()}
self.data = data
def process(self):
self.name = self.data["subset"]
node_name = self.data["subset"].replace(
"_", "_f{}_".format(nuke.frame()))
instance = nuke.toNode(self.data["subset"])
self.data["subset"] = node_name
family = self.family
node = 'write'
if not instance:
write_data = {
"frame_range": [nuke.frame(), nuke.frame()],
"class": node,
"preset": self.preset,
"avalon": self.data
}
nuke.createNode("FrameHold", "first_frame {}".format(nuke.frame()))
create_write_node(node_name, write_data)
return
"""

View file

@ -11,7 +11,7 @@ class SelectContainers(api.InventoryAction):
import avalon.nuke
nodes = [i["_tool"] for i in containers]
nodes = [i["_node"] for i in containers]
with avalon.nuke.viewer_update_and_undo_stop():
# clear previous_selection

View file

@ -20,7 +20,7 @@
#
# # Get tool color
# first = containers[0]
# tool = first["_tool"]
# tool = first["_node"]
# color = tool.TileColor
#
# if color is not None:
@ -40,7 +40,7 @@
# rgb_f_table = {"R": rgb_f[0], "G": rgb_f[1], "B": rgb_f[2]}
#
# # Update tool
# tool = container["_tool"]
# tool = container["_node"]
# tool.TileColor = rgb_f_table
#
# result.append(container)

View file

@ -3,7 +3,7 @@ import os
import nuke
import pyblish.api
from avalon import io, api
from pype.nuke.lib import get_avalon_knob_data
from avalon.nuke.lib import get_avalon_knob_data
@pyblish.api.log
@ -34,6 +34,7 @@ class CollectNukeInstances(pyblish.api.ContextPlugin):
# get data from avalon knob
avalon_knob_data = get_avalon_knob_data(node)
self.log.debug("avalon_knob_data: {}".format(avalon_knob_data))
if not avalon_knob_data:
continue

View file

@ -28,7 +28,7 @@ def get_version_from_path(file):
v: version number in string ('001')
"""
pattern = re.compile(r"[\._]v([0-9]*)")
pattern = re.compile(r"v([0-9]*)")
try:
v = pattern.findall(file)[0]
return v

View file

@ -0,0 +1,54 @@
'''
Simple socket server using threads
'''
import socket
import sys
import threading
import StringIO
import contextlib
import nuke
HOST = ''
PORT = 8888
@contextlib.contextmanager
def stdoutIO(stdout=None):
old = sys.stdout
if stdout is None:
stdout = StringIO.StringIO()
sys.stdout = stdout
yield stdout
sys.stdout = old
def _exec(data):
with stdoutIO() as s:
exec(data)
return s.getvalue()
def server_start():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
while 1:
client, address = s.accept()
try:
data = client.recv(4096)
if data:
result = nuke.executeInMainThreadWithResult(_exec, args=(data))
client.send(str(result))
except SystemExit:
result = self.encode('SERVER: Shutting down...')
client.send(str(result))
raise
finally:
client.close()
t = threading.Thread(None, server_start)
t.setDaemon(True)
t.start()

View file

@ -1,3 +1,4 @@
import atom_server
from pype.nuke.lib import (
writes_version_sync,
@ -15,5 +16,6 @@ log = Logger().get_logger(__name__, "nuke")
nuke.addOnScriptSave(onScriptLoad)
nuke.addOnScriptLoad(checkInventoryVersions)
nuke.addOnScriptSave(checkInventoryVersions)
nuke.addOnScriptSave(writes_version_sync)
log.info('Automatic syncing of write file knob to script version')