Merge branch 'refs/heads/develop' into release/2.3.0

This commit is contained in:
Milan Kolar 2019-11-06 10:23:07 +01:00
commit 45b2ec6020
22 changed files with 224 additions and 63 deletions

View file

@ -15,6 +15,7 @@ log = Logger().get_logger(__name__)
class RVAction(BaseAction):
""" Launch RV action """
ignore_me = "rv" not in config.get_presets()
identifier = "rv.launch.action"
label = "rv"
description = "rv Launcher"
@ -42,8 +43,9 @@ class RVAction(BaseAction):
)
else:
# if not, fallback to config file location
self.config_data = config.get_presets()['rv']['config']
self.set_rv_path()
if "rv" in config.get_presets():
self.config_data = config.get_presets()['rv']['config']
self.set_rv_path()
if self.rv_path is None:
return

View file

@ -20,7 +20,8 @@ class ThumbnailEvents(BaseEvent):
if parent.get('thumbnail') and not task.get('thumbnail'):
task['thumbnail'] = parent['thumbnail']
self.log.info('>>> Updated thumbnail on [ %s/%s ]'.format(
parent['name'], task['name']))
parent['name'], task['name']
))
# Update task thumbnail from published version
# if (entity['entityType'] == 'assetversion' and
@ -32,18 +33,24 @@ class ThumbnailEvents(BaseEvent):
version = session.get('AssetVersion', entity['entityId'])
thumbnail = version.get('thumbnail')
task = version['task']
if thumbnail:
task['thumbnail'] = thumbnail
task['parent']['thumbnail'] = thumbnail
self.log.info('>>> Updating thumbnail for task and shot\
[ {} ]'.format(task['name']))
parent = version['asset']['parent']
task = version['task']
parent['thumbnail_id'] = version['thumbnail_id']
if parent.entity_type.lower() == "project":
name = parent["full_name"]
else:
name = parent["name"]
msg = '>>> Updating thumbnail for shot [ {} ]'.format(name)
if task:
task['thumbnail_id'] = version['thumbnail_id']
msg += " and task [ {} ]".format(task["name"]))
self.log.info(msg)
session.commit()
pass
def register(session, plugins_presets):
'''Register plugin. Called when used as an plugin.'''

View file

@ -8,7 +8,9 @@ import avalon.nuke
import pype.api as pype
import nuke
from .templates import (
from .presets import (
get_colorspace_preset,
get_node_dataflow_preset,
get_node_colorspace_preset
@ -55,7 +57,8 @@ def checkInventoryVersions():
if container:
node = container["_node"]
avalon_knob_data = avalon.nuke.get_avalon_knob_data(node)
avalon_knob_data = avalon.nuke.get_avalon_knob_data(
node, ['avalon:', 'ak:'])
# get representation from io
representation = io.find_one({
@ -101,7 +104,8 @@ def writes_version_sync():
for each in nuke.allNodes():
if each.Class() == 'Write':
avalon_knob_data = avalon.nuke.get_avalon_knob_data(each)
avalon_knob_data = avalon.nuke.get_avalon_knob_data(
each, ['avalon:', 'ak:'])
try:
if avalon_knob_data['families'] not in ["render"]:
@ -134,7 +138,8 @@ def get_render_path(node):
''' Generate Render path from presets regarding avalon knob data
'''
data = dict()
data['avalon'] = avalon.nuke.get_avalon_knob_data(node)
data['avalon'] = avalon.nuke.get_avalon_knob_data(
node, ['avalon:', 'ak:'])
data_preset = {
"class": data['avalon']['family'],
@ -379,6 +384,10 @@ def add_rendering_knobs(node):
knob = nuke.Boolean_Knob("render_farm", "Render on Farm")
knob.setValue(False)
node.addKnob(knob)
if "review" not in node.knobs():
knob = nuke.Boolean_Knob("review", "Review")
knob.setValue(True)
node.addKnob(knob)
return node
@ -389,6 +398,14 @@ def add_deadline_tab(node):
knob.setValue(1)
node.addKnob(knob)
knob = nuke.Int_Knob("deadlinePriority", "Priority")
knob.setValue(50)
node.addKnob(knob)
def get_deadline_knob_names():
return ["Deadline", "deadlineChunkSize", "deadlinePriority"]
def create_backdrop(label="", color=None, layer=0,
nodes=None):
@ -543,17 +560,34 @@ class WorkfileSettings(object):
assert isinstance(root_dict, dict), log.error(
"set_root_colorspace(): argument should be dictionary")
log.debug(">> root_dict: {}".format(root_dict))
# first set OCIO
if self._root_node["colorManagement"].value() \
not in str(root_dict["colorManagement"]):
self._root_node["colorManagement"].setValue(
str(root_dict["colorManagement"]))
log.debug("nuke.root()['{0}'] changed to: {1}".format(
"colorManagement", root_dict["colorManagement"]))
root_dict.pop("colorManagement")
# second set ocio version
if self._root_node["OCIO_config"].value() \
not in str(root_dict["OCIO_config"]):
self._root_node["OCIO_config"].setValue(
str(root_dict["OCIO_config"]))
log.debug("nuke.root()['{0}'] changed to: {1}".format(
"OCIO_config", root_dict["OCIO_config"]))
root_dict.pop("OCIO_config")
# third set ocio custom path
if root_dict.get("customOCIOConfigPath"):
self._root_node["customOCIOConfigPath"].setValue(
str(root_dict["customOCIOConfigPath"]).format(**os.environ)
)
log.debug("nuke.root()['{}'] changed to: {}".format(
"customOCIOConfigPath", root_dict["customOCIOConfigPath"]))
root_dict.pop("customOCIOConfigPath")
# then set the rest
for knob, value in root_dict.items():
@ -798,10 +832,12 @@ def get_write_node_template_attr(node):
'''
# get avalon data from node
data = dict()
data['avalon'] = avalon.nuke.get_avalon_knob_data(node)
data['avalon'] = avalon.nuke.get_avalon_knob_data(
node, ['avalon:', 'ak:'])
data_preset = {
"class": data['avalon']['family'],
"preset": data['avalon']['families']
"families": data['avalon']['families'],
"preset": data['avalon']['families'] # omit < 2.0.0v
}
# get template data
@ -927,7 +963,7 @@ class BuildWorkfile(WorkfileSettings):
def process(self,
regex_filter=None,
version=None,
representations=["exr", "dpx", "lutJson"]):
representations=["exr", "dpx", "lutJson", "mov", "preview"]):
"""
A short description.
@ -984,6 +1020,8 @@ class BuildWorkfile(WorkfileSettings):
version=version,
representations=representations)
log.info("__ subsets: `{}`".format(subsets))
nodes_backdrop = list()
for name, subset in subsets.items():

View file

@ -25,16 +25,22 @@ def get_node_dataflow_preset(**kwarg):
log.info(kwarg)
host = kwarg.get("host", "nuke")
cls = kwarg.get("class", None)
preset = kwarg.get("preset", None)
assert any([host, cls]), log.error("nuke.templates.get_node_dataflow_preset(): \
Missing mandatory kwargs `host`, `cls`")
families = kwarg.get("families", [])
preset = kwarg.get("preset", None) # omit < 2.0.0v
assert any([host, cls]), log.error(
"`{}`: Missing mandatory kwargs `host`, `cls`".format(__file__))
nuke_dataflow = get_dataflow_preset().get(str(host), None)
nuke_dataflow_nodes = nuke_dataflow.get('nodes', None)
nuke_dataflow_node = nuke_dataflow_nodes.get(str(cls), None)
if preset:
if preset: # omit < 2.0.0v
nuke_dataflow_node = nuke_dataflow_node.get(str(preset), None)
# omit < 2.0.0v
for family in families:
nuke_dataflow_node = nuke_dataflow_node.get(str(family), None)
log.info("Dataflow: {}".format(nuke_dataflow_node))
return nuke_dataflow_node
@ -47,8 +53,8 @@ def get_node_colorspace_preset(**kwarg):
host = kwarg.get("host", "nuke")
cls = kwarg.get("class", None)
preset = kwarg.get("preset", None)
assert any([host, cls]), log.error("nuke.templates.get_node_colorspace_preset(): \
Missing mandatory kwargs `host`, `cls`")
assert any([host, cls]), log.error(
"`{}`: Missing mandatory kwargs `host`, `cls`".format(__file__))
nuke_colorspace = get_colorspace_preset().get(str(host), None)
nuke_colorspace_node = nuke_colorspace.get(str(cls), None)

View file

@ -314,8 +314,14 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
index_frame_start = int(repre.get("frameStart"))
dst_padding_exp = src_padding_exp
dst_start_frame = None
for i in src_collection.indexes:
src_padding = src_padding_exp % i
# for adding first frame into db
if not dst_start_frame:
dst_start_frame = src_padding
src_file_name = "{0}{1}{2}".format(
src_head, src_padding, src_tail)
@ -326,19 +332,22 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
dst_padding = dst_padding_exp % index_frame_start
index_frame_start += 1
dst = "{0}{1}{2}".format(dst_head, dst_padding, dst_tail).replace("..", ".")
dst = "{0}{1}{2}".format(
dst_head,
dst_padding,
dst_tail).replace("..", ".")
self.log.debug("destination: `{}`".format(dst))
src = os.path.join(stagingdir, src_file_name)
self.log.debug("source: {}".format(src))
instance.data["transfers"].append([src, dst])
repre['published_path'] = "{0}{1}{2}".format(dst_head,
dst_padding_exp,
dst_tail)
# for imagesequence version data
hashes = '#' * len(dst_padding)
dst = os.path.normpath("{0}{1}{2}".format(
dst_head, hashes, dst_tail))
dst = "{0}{1}{2}".format(
dst_head,
dst_start_frame,
dst_tail).replace("..", ".")
repre['published_path'] = dst
else:
# Single file

View file

@ -174,7 +174,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
"JobDependency0": job["_id"],
"UserName": job["Props"]["User"],
"Comment": instance.context.data.get("comment", ""),
"InitialStatus": state
"InitialStatus": state,
"Priority": job["Props"]["Pri"]
},
"PluginInfo": {
"Version": "3.6",

View file

@ -46,7 +46,8 @@ class ValidateFtrackAttributes(pyblish.api.InstancePlugin):
"Missing FTrack Task entity in context")
host = pyblish.api.current_host()
to_check = context.data["presets"][host].get("ftrack_attributes")
to_check = context.data["presets"].get(
host, {}).get("ftrack_attributes")
if not to_check:
self.log.warning("ftrack_attributes preset not found")
return

View file

@ -1,7 +1,6 @@
from avalon import api
import pype.maya.plugin
import os
import pymel.core as pm
from pypeapp import config
@ -70,6 +69,7 @@ class AssProxyLoader(pype.maya.plugin.ReferenceLoader):
import os
from maya import cmds
import pymel.core as pm
node = container["objectName"]

View file

@ -2,7 +2,6 @@ from avalon import api
import pype.maya.plugin
import os
from pypeapp import config
import pymel.core as pm
reload(config)

View file

@ -1,5 +1,3 @@
import pymel.core as pc
from avalon import api
from Qt import QtWidgets
@ -14,6 +12,8 @@ class ImagePlaneLoader(api.Loader):
color = "orange"
def load(self, context, name, namespace, data):
import pymel.core as pc
new_nodes = []
image_plane_depth = 1000

View file

@ -1,8 +1,6 @@
import pype.maya.plugin
import os
from pypeapp import config
import pymel.core as pm
reload(config)
import pype.maya.plugin
reload(pype.maya.plugin)
@ -20,9 +18,10 @@ class ReferenceLoader(pype.maya.plugin.ReferenceLoader):
color = "orange"
def process_reference(self, context, name, namespace, data):
import maya.cmds as cmds
from avalon import maya
import pymel.core as pm
try:
family = context["representation"]["context"]["family"]

View file

@ -1,9 +1,17 @@
import pype.maya.plugin
import os
from collections import defaultdict
from pypeapp import config
import pype.maya.plugin
from pype.maya import lib
class YetiRigLoader(pype.maya.plugin.ReferenceLoader):
"""
This loader will load Yeti rig. You can select something in scene and if it
has same ID as mesh published with rig, their shapes will be linked
together.
"""
families = ["yetiRig"]
representations = ["ma"]
@ -18,6 +26,32 @@ class YetiRigLoader(pype.maya.plugin.ReferenceLoader):
import maya.cmds as cmds
from avalon import maya
# get roots of selected hierarchies
selected_roots = []
for sel in cmds.ls(sl=True, long=True):
selected_roots.append(sel.split("|")[1])
# get all objects under those roots
selected_hierarchy = []
for root in selected_roots:
selected_hierarchy.append(cmds.listRelatives(
root,
allDescendents=True) or [])
# flatten the list and filter only shapes
shapes_flat = []
for root in selected_hierarchy:
shapes = cmds.ls(root, long=True, type="mesh") or []
for shape in shapes:
shapes_flat.append(shape)
# create dictionary of cbId and shape nodes
scene_lookup = defaultdict(list)
for node in shapes_flat:
cb_id = lib.get_id(node)
scene_lookup[cb_id] = node
# load rig
with maya.maintained_selection():
nodes = cmds.file(self.fname,
namespace=namespace,
@ -26,6 +60,20 @@ class YetiRigLoader(pype.maya.plugin.ReferenceLoader):
groupReference=True,
groupName="{}:{}".format(namespace, name))
# for every shape node we've just loaded find matching shape by its
# cbId in selection. If found outMesh of scene shape will connect to
# inMesh of loaded shape.
for destination_node in nodes:
source_node = scene_lookup[lib.get_id(destination_node)]
if source_node:
self.log.info("found: {}".format(source_node))
self.log.info(
"creating connection to {}".format(destination_node))
cmds.connectAttr("{}.outMesh".format(source_node),
"{}.inMesh".format(destination_node),
force=True)
groupName = "{}:{}".format(namespace, name)
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
@ -38,6 +86,4 @@ class YetiRigLoader(pype.maya.plugin.ReferenceLoader):
c[0], c[1], c[2])
self[:] = nodes
self.log.info("Yeti Rig Connection Manager will be available soon")
return nodes

View file

@ -126,6 +126,18 @@ class ExtractYetiRig(pype.api.Extractor):
with open(settings_path, "w") as fp:
json.dump(settings, fp, ensure_ascii=False)
# add textures to transfers
if 'transfers' not in instance.data:
instance.data['transfers'] = []
for resource in instance.data.get('resources', []):
for file in resource['files']:
src = file
dst = os.path.join(image_search_path, os.path.basename(file))
instance.data['transfers'].append([src, dst])
self.log.info("adding transfer {} -> {}". format(src, dst))
# Ensure the imageSearchPath is being remapped to the publish folder
attr_value = {"%s.imageSearchPath" % n: str(image_search_path) for
n in yeti_nodes}

View file

@ -110,6 +110,7 @@ class LoadSequence(api.Loader):
last += self.handle_end
file = self.fname.replace("\\", "/")
log.info("file: {}\n".format(self.fname))
repr_cont = context["representation"]["context"]
@ -118,6 +119,11 @@ class LoadSequence(api.Loader):
repr_cont["subset"],
repr_cont["representation"])
if "#" not in file:
frame = repr_cont.get("frame")
padding = len(frame)
file = file.replace(frame, "#"*padding)
# Create the Loader with the filename path set
with viewer_update_and_undo_stop():
# TODO: it might be universal read to img/geo/camera

View file

@ -1,5 +1,5 @@
import pyblish.api
import nuke
class CollectReview(pyblish.api.InstancePlugin):
"""Collect review instance from rendered frames
@ -9,9 +9,20 @@ class CollectReview(pyblish.api.InstancePlugin):
family = "review"
label = "Collect Review"
hosts = ["nuke"]
families = ["render", "render.local"]
families = ["render", "render.local", "render.farm"]
def process(self, instance):
if instance.data["families"]:
instance.data["families"].append("review")
self.log.info("Review collected: `{}`".format(instance))
node = instance[0]
if "review" not in node.knobs():
knob = nuke.Boolean_Knob("review", "Review")
knob.setValue(True)
node.addKnob(knob)
if not node["review"].value():
return
instance.data["families"].append("review")
instance.data['families'].append('ftrack')
self.log.info("Review collected: `{}`".format(instance))

View file

@ -65,7 +65,6 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
)
if 'render' in instance.data['families']:
instance.data['families'].append('ftrack')
if "representations" not in instance.data:
instance.data["representations"] = list()
@ -78,22 +77,22 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
try:
collected_frames = os.listdir(output_dir)
if collected_frames:
representation['frameStart'] = "%0{}d".format(
len(str(last_frame))) % first_frame
representation['files'] = collected_frames
instance.data["representations"].append(representation)
except Exception:
instance.data["representations"].append(representation)
self.log.debug("couldn't collect frames: {}".format(label))
if 'render.local' in instance.data['families']:
instance.data['families'].append('ftrack')
# Add version data to instance
version_data = {
"handles": handle_start,
"handleStart": handle_start,
"handleEnd": handle_end,
"frameStart": first_frame,
"frameEnd": last_frame,
"frameStart": first_frame + handle_start,
"frameEnd": last_frame - handle_end,
"version": int(version),
"colorspace": node["colorspace"].value(),
"families": [instance.data["family"]],
@ -106,6 +105,10 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
if "deadlineChunkSize" in group_node.knobs():
deadlineChunkSize = group_node["deadlineChunkSize"].value()
deadlinePriority = 50
if "deadlinePriority" in group_node.knobs():
deadlinePriority = group_node["deadlinePriority"].value()
instance.data.update({
"versionData": version_data,
"path": path,
@ -117,7 +120,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
"frameEnd": last_frame,
"outputType": output_type,
"colorspace": node["colorspace"].value(),
"deadlineChunkSize": deadlineChunkSize
"deadlineChunkSize": deadlineChunkSize,
"deadlinePriority": deadlinePriority
})
self.log.debug("instance.data: {}".format(instance.data))

View file

@ -58,6 +58,7 @@ class NukeRenderLocal(pype.api.Extractor):
repre = {
'name': ext,
'ext': ext,
'frameStart': "%0{}d".format(len(str(last_frame))) % first_frame,
'files': collected_frames,
"stagingDir": out_dir,
"anatomy_template": "render"

View file

@ -85,6 +85,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin):
end=int(instance.data["frameEnd"])
),
"ChunkSize": instance.data["deadlineChunkSize"],
"Priority": instance.data["deadlinePriority"],
"Comment": comment,

View file

@ -22,6 +22,13 @@ class RepairNukeWriteDeadlineTab(pyblish.api.Action):
for instance in instances:
group_node = [x for x in instance if x.Class() == "Group"][0]
# Remove exising knobs.
knob_names = pype.nuke.lib.get_deadline_knob_names()
for name, knob in group_node.knobs().iteritems():
if name in knob_names:
group_node.removeKnob(knob)
pype.nuke.lib.add_deadline_tab(group_node)
@ -38,5 +45,9 @@ class ValidateNukeWriteDeadlineTab(pyblish.api.InstancePlugin):
def process(self, instance):
group_node = [x for x in instance if x.Class() == "Group"][0]
msg = "Deadline tab missing on \"{}\"".format(group_node.name())
assert "Deadline" in group_node.knobs(), msg
knob_names = pype.nuke.lib.get_deadline_knob_names()
missing_knobs = []
for name in knob_names:
if name not in group_node.knobs().keys():
missing_knobs.append(name)
assert not missing_knobs, "Missing knobs: {}".format(missing_knobs)

View file

@ -196,12 +196,14 @@ class CollectPlatesData(api.InstancePlugin):
thumb_file = head + ".png"
thumb_path = os.path.join(staging_dir, thumb_file)
thumb_frame = instance.data["sourceIn"] + ((instance.data["sourceOut"] - instance.data["sourceIn"])/2)
thumbnail = item.thumbnail(instance.data["sourceIn"]).save(
thumbnail = item.thumbnail(thumb_frame).save(
thumb_path,
format='png'
)
self.log.debug("__ thumbnail: {}".format(thumbnail))
self.log.debug("__ sourceIn: `{}`".format(instance.data["sourceIn"]))
self.log.debug("__ thumbnail: `{}`, frame: `{}`".format(thumbnail, thumb_frame))
thumb_representation = {
'files': thumb_file,

View file

@ -106,7 +106,6 @@ class CollectReviews(api.InstancePlugin):
def create_thumbnail(self, instance):
item = instance.data["item"]
source_in = instance.data["sourceIn"]
source_path = instance.data["sourcePath"]
source_file = os.path.basename(source_path)
@ -119,11 +118,17 @@ class CollectReviews(api.InstancePlugin):
thumb_file = head + ".png"
thumb_path = os.path.join(staging_dir, thumb_file)
self.log.debug("__ thumb_path: {}".format(thumb_path))
self.log.debug("__ source_in: {}".format(source_in))
thumbnail = item.thumbnail(source_in).save(
thumb_frame = instance.data["sourceIn"] + ((instance.data["sourceOut"] - instance.data["sourceIn"])/2)
thumbnail = item.thumbnail(thumb_frame).save(
thumb_path,
format='png'
)
self.log.debug("__ sourceIn: `{}`".format(instance.data["sourceIn"]))
self.log.debug("__ thumbnail: `{}`, frame: `{}`".format(thumbnail, thumb_frame))
self.log.debug("__ thumbnail: {}".format(thumbnail))
thumb_representation = {