Merge branch 'develop' into bugfix/PYPE-436-fix-collect-assumed-destination

This commit is contained in:
Jakub Jezek 2019-10-07 13:15:38 +02:00
commit 66bf8b5bb9
30 changed files with 448 additions and 110 deletions

View file

@ -35,6 +35,8 @@ def patched_discover(superclass):
plugins = _original_discover(superclass)
# determine host application to use for finding presets
if avalon.registered_host() is None:
return plugins
host = avalon.registered_host().__name__.split(".")[-1]
# map plugin superclass to preset json. Currenly suppoted is load and

View file

@ -23,10 +23,10 @@ class CustomAttributeDoctor(BaseAction):
icon = '{}/ftrack/action_icons/PypeDoctor.svg'.format(
os.environ.get('PYPE_STATICS_SERVER', '')
)
hierarchical_ca = ['handle_start', 'handle_end', 'fstart', 'fend']
hierarchical_ca = ['handleStart', 'handleEnd', 'frameStart', 'frameEnd']
hierarchical_alternatives = {
'handle_start': 'handles',
'handle_end': 'handles'
'handleStart': 'handles',
'handleEnd': 'handles'
}
# Roles for new custom attributes
@ -34,22 +34,22 @@ class CustomAttributeDoctor(BaseAction):
write_roles = ['ALL',]
data_ca = {
'handle_start': {
'handleStart': {
'label': 'Frame handles start',
'type': 'number',
'config': json.dumps({'isdecimal': False})
},
'handle_end': {
'handleEnd': {
'label': 'Frame handles end',
'type': 'number',
'config': json.dumps({'isdecimal': False})
},
'fstart': {
'frameStart': {
'label': 'Frame start',
'type': 'number',
'config': json.dumps({'isdecimal': False})
},
'fend': {
'frameEnd': {
'label': 'Frame end',
'type': 'number',
'config': json.dumps({'isdecimal': False})

View file

@ -39,19 +39,17 @@ SHAPE_ATTRS = {"castsShadows",
"doubleSided",
"opposite"}
RENDER_ATTRS = {"vray":
{
RENDER_ATTRS = {"vray": {
"node": "vraySettings",
"prefix": "fileNamePrefix",
"padding": "fileNamePadding",
"ext": "imageFormatStr"
},
"default":
{
},
"default": {
"node": "defaultRenderGlobals",
"prefix": "imageFilePrefix",
"padding": "extensionPadding"
}
}
}
@ -341,19 +339,6 @@ def undo_chunk():
cmds.undoInfo(closeChunk=True)
@contextlib.contextmanager
def renderlayer(layer):
"""Set the renderlayer during the context"""
original = cmds.editRenderLayerGlobals(query=True, currentRenderLayer=True)
try:
cmds.editRenderLayerGlobals(currentRenderLayer=layer)
yield
finally:
cmds.editRenderLayerGlobals(currentRenderLayer=original)
@contextlib.contextmanager
def evaluation(mode="off"):
"""Set the evaluation manager during context.
@ -832,7 +817,8 @@ def is_visible(node,
# Display layers set overrideEnabled and overrideVisibility on members
if cmds.attributeQuery('overrideEnabled', node=node, exists=True):
override_enabled = cmds.getAttr('{}.overrideEnabled'.format(node))
override_visibility = cmds.getAttr('{}.overrideVisibility'.format(node))
override_visibility = cmds.getAttr('{}.overrideVisibility'.format(
node))
if override_enabled and override_visibility:
return False
@ -854,8 +840,8 @@ def extract_alembic(file,
startFrame=None,
endFrame=None,
selection=True,
uvWrite= True,
eulerFilter= True,
uvWrite=True,
eulerFilter=True,
dataFormat="ogawa",
verbose=False,
**kwargs):
@ -1470,8 +1456,8 @@ def apply_shaders(relationships, shadernodes, nodes):
member_uuids = [member["uuid"] for member in data["members"]]
filtered_nodes = list()
for uuid in member_uuids:
filtered_nodes.extend(nodes_by_id[uuid])
for m_uuid in member_uuids:
filtered_nodes.extend(nodes_by_id[m_uuid])
id_shading_engines = shading_engines_by_id[shader_uuid]
if not id_shading_engines:
@ -2110,6 +2096,7 @@ def bake_to_world_space(nodes,
return world_space_nodes
def load_capture_preset(path=None, data=None):
import capture_gui
import capture
@ -2119,14 +2106,14 @@ def load_capture_preset(path=None, data=None):
else:
path = path
preset = capture_gui.lib.load_json(path)
print preset
print(preset)
options = dict()
# CODEC
id = 'Codec'
for key in preset[id]:
options[str(key)]= preset[id][key]
options[str(key)] = preset[id][key]
# GENERIC
id = 'Generic'
@ -2142,7 +2129,6 @@ def load_capture_preset(path=None, data=None):
options['height'] = preset[id]['height']
options['width'] = preset[id]['width']
# DISPLAY OPTIONS
id = 'Display Options'
disp_options = {}
@ -2154,7 +2140,6 @@ def load_capture_preset(path=None, data=None):
options['display_options'] = disp_options
# VIEWPORT OPTIONS
temp_options = {}
id = 'Renderer'
@ -2163,11 +2148,12 @@ def load_capture_preset(path=None, data=None):
temp_options2 = {}
id = 'Viewport Options'
light_options = { 0: "default",
1: 'all',
2: 'selected',
3: 'flat',
4: 'nolights'}
light_options = {
0: "default",
1: 'all',
2: 'selected',
3: 'flat',
4: 'nolights'}
for key in preset[id]:
if key == 'high_quality':
temp_options2['multiSampleEnable'] = True
@ -2190,7 +2176,10 @@ def load_capture_preset(path=None, data=None):
else:
temp_options[str(key)] = preset[id][key]
for key in ['override_viewport_options', 'high_quality', 'alphaCut', "gpuCacheDisplayFilter"]:
for key in ['override_viewport_options',
'high_quality',
'alphaCut',
'gpuCacheDisplayFilter']:
temp_options.pop(key, None)
for key in ['ssaoEnable']:
@ -2199,7 +2188,6 @@ def load_capture_preset(path=None, data=None):
options['viewport_options'] = temp_options
options['viewport2_options'] = temp_options2
# use active sound track
scene = capture.parse_active_scene()
options['sound'] = scene['sound']
@ -2363,31 +2351,51 @@ class shelf():
if item['type'] == 'button':
self.addButon(item['name'], command=item['command'])
if item['type'] == 'menuItem':
self.addMenuItem(item['parent'], item['name'], command=item['command'])
self.addMenuItem(item['parent'],
item['name'],
command=item['command'])
if item['type'] == 'subMenu':
self.addMenuItem(item['parent'], item['name'], command=item['command'])
self.addMenuItem(item['parent'],
item['name'],
command=item['command'])
def addButon(self, label, icon="commandButton.png", command=_null, doubleCommand=_null):
'''Adds a shelf button with the specified label, command, double click command and image.'''
def addButon(self, label, icon="commandButton.png",
command=_null, doubleCommand=_null):
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
cmds.setParent(self.name)
if icon:
icon = self.iconPath + icon
cmds.shelfButton(width=37, height=37, image=icon, l=label, command=command, dcc=doubleCommand, imageOverlayLabel=label, olb=self.labelBackground, olc=self.labelColour)
cmds.shelfButton(width=37, height=37, image=icon, label=label,
command=command, dcc=doubleCommand,
imageOverlayLabel=label, olb=self.labelBackground,
olc=self.labelColour)
def addMenuItem(self, parent, label, command=_null, icon=""):
'''Adds a shelf button with the specified label, command, double click command and image.'''
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
if icon:
icon = self.iconPath + icon
return cmds.menuItem(p=parent, l=label, c=command, i="")
return cmds.menuItem(p=parent, label=label, c=command, i="")
def addSubMenu(self, parent, label, icon=None):
'''Adds a sub menu item with the specified label and icon to the specified parent popup menu.'''
'''
Adds a sub menu item with the specified label and icon to
the specified parent popup menu.
'''
if icon:
icon = self.iconPath + icon
return cmds.menuItem(p=parent, l=label, i=icon, subMenu=1)
return cmds.menuItem(p=parent, label=label, i=icon, subMenu=1)
def _cleanOldShelf(self):
'''Checks if the shelf exists and empties it if it does or creates it if it does not.'''
'''
Checks if the shelf exists and empties it if it does
or creates it if it does not.
'''
if cmds.shelfLayout(self.name, ex=1):
if cmds.shelfLayout(self.name, q=1, ca=1):
for each in cmds.shelfLayout(self.name, q=1, ca=1):

View file

@ -17,6 +17,9 @@ class IntegrateCleanComponentData(pyblish.api.InstancePlugin):
for comp in instance.data['representations']:
self.log.debug('component {}'.format(comp))
if "%" in comp['published_path'] or "#" in comp['published_path']:
continue
if comp.get('thumbnail') or ("thumbnail" in comp.get('tags', [])):
os.remove(comp['published_path'])

View file

@ -46,9 +46,25 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin):
in_data = json.load(f)
asset_name = in_data['asset']
family_preset_key = in_data.get('family_preset_key', '')
family = in_data['family']
subset = in_data['subset']
# Load presets
presets = context.data.get("presets")
if not presets:
from pypeapp import config
presets = config.get_presets()
# Get from presets anatomy key that will be used for getting template
# - default integrate new is used if not set
anatomy_key = presets.get(
"standalone_publish", {}).get(
"families", {}).get(
family_preset_key, {}).get(
"anatomy_template"
)
project = io.find_one({'type': 'project'})
asset = io.find_one({
'type': 'asset',
@ -65,6 +81,8 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin):
"label": subset,
"name": subset,
"family": family,
"frameStart": in_data.get("representations", [None])[0].get("frameStart", None),
"frameEnd": in_data.get("representations", [None])[0].get("frameEnd", None),
"families": [family, 'ftrack'],
})
self.log.info("collected instance: {}".format(instance.data))
@ -78,7 +96,9 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin):
component['destination'] = component['files']
component['stagingDir'] = component['stagingDir']
component['anatomy_template'] = 'render'
# Do not set anatomy_template if not specified
if anatomy_key:
component['anatomy_template'] = anatomy_key
if isinstance(component['files'], list):
collections, remainder = clique.assemble(component['files'])
self.log.debug("collecting sequence: {}".format(collections))

View file

@ -15,7 +15,7 @@ class CollectOutputRepreConfig(pyblish.api.ContextPlugin):
order = pyblish.api.CollectorOrder
label = "Collect Config for representation"
hosts = ["shell"]
hosts = ["shell", "standalonepublisher"]
def process(self, context):
config_data = config.get_presets()["ftrack"]["output_representation"]

View file

@ -14,7 +14,7 @@ from pypeapp import config
class CollectPresets(api.ContextPlugin):
"""Collect Presets."""
order = api.CollectorOrder
order = api.CollectorOrder - 0.491
label = "Collect Presets"
def process(self, context):

View file

@ -13,6 +13,8 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
label = 'Collect Version'
def process(self, context):
if "standalonepublisher" in context.data.get("host"):
return
filename = os.path.basename(context.data.get('currentFile'))

View file

@ -0,0 +1,126 @@
import os
import tempfile
import subprocess
import pyblish.api
import pype.api
class ExtractThumbnail(pyblish.api.InstancePlugin):
"""Extract jpeg thumbnail from component input from standalone publisher
Uses jpeg file from component if possible (when single or multiple jpegs
are loaded to component selected as thumbnail) otherwise extracts from
input file/s single jpeg to temp.
"""
label = "Extract Thumbnail"
hosts = ["standalonepublisher"]
order = pyblish.api.ExtractorOrder
def process(self, instance):
repres = instance.data.get('representations')
if not repres:
return
thumbnail_repre = None
for repre in repres:
if repre.get("thumbnail"):
thumbnail_repre = repre
break
if not thumbnail_repre:
return
files = thumbnail_repre.get("files")
if not files:
return
if isinstance(files, list):
files_len = len(files)
file = str(files[0])
else:
files_len = 1
file = files
is_jpeg = False
if file.endswith(".jpeg") or file.endswith(".jpg"):
is_jpeg = True
if is_jpeg and files_len == 1:
# skip if already is single jpeg file
return
elif is_jpeg:
# use first frame as thumbnail if is sequence of jpegs
full_thumbnail_path = file
self.log.info(
"For thumbnail is used file: {}".format(full_thumbnail_path)
)
else:
# Convert to jpeg if not yet
full_input_path = os.path.join(thumbnail_repre["stagingDir"], file)
self.log.info("input {}".format(full_input_path))
full_thumbnail_path = tempfile.mkstemp(suffix=".jpg")[1]
self.log.info("output {}".format(full_thumbnail_path))
config_data = instance.context.data.get("output_repre_config", {})
proj_name = os.environ.get("AVALON_PROJECT", "__default__")
profile = config_data.get(
proj_name,
config_data.get("__default__", {})
)
ffmpeg_path = os.getenv("FFMPEG_PATH", "")
if ffmpeg_path:
ffmpeg_path += "/ffmpeg"
else:
ffmpeg_path = "ffmpeg"
jpeg_items = []
jpeg_items.append(ffmpeg_path)
# override file if already exists
jpeg_items.append("-y")
# add input filters from peresets
if profile:
jpeg_items.extend(profile.get('input', []))
# input file
jpeg_items.append("-i {}".format(full_input_path))
# extract only single file
jpeg_items.append("-vframes 1")
# output file
jpeg_items.append(full_thumbnail_path)
subprocess_jpeg = " ".join(jpeg_items)
# run subprocess
self.log.debug("Executing: {}".format(subprocess_jpeg))
subprocess.Popen(
subprocess_jpeg,
stdout=subprocess.PIPE,
shell=True
)
# remove thumbnail key from origin repre
thumbnail_repre.pop("thumbnail")
filename = os.path.basename(full_thumbnail_path)
staging_dir = os.path.dirname(full_thumbnail_path)
# create new thumbnail representation
representation = {
'name': 'jpg',
'ext': 'jpg',
'files': filename,
"stagingDir": staging_dir,
"thumbnail": True,
"tags": []
}
# # add Delete tag when temp file was rendered
# if not is_jpeg:
# representation["tags"].append("delete")
instance.data["representations"].append(representation)

View file

@ -307,7 +307,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
if repre.get("frameStart"):
frame_start_padding = len(str(
repre.get("frameEnd")))
index_frame_start = repre.get("frameStart")
index_frame_start = int(repre.get("frameStart"))
dst_padding_exp = src_padding_exp
for i in src_collection.indexes:
@ -322,7 +322,7 @@ 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)
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))
@ -357,7 +357,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
src = os.path.join(stagingdir, fname)
anatomy_filled = anatomy.format(template_data)
dst = os.path.normpath(
anatomy_filled[template_name]["path"])
anatomy_filled[template_name]["path"]).replace("..", ".")
instance.data["transfers"].append([src, dst])
@ -440,6 +440,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
Returns:
None
"""
src = os.path.normpath(src)
dst = os.path.normpath(dst)
self.log.debug("Copying file .. {} -> {}".format(src, dst))
dirname = os.path.dirname(dst)

View file

@ -7,5 +7,5 @@ class CreateAssembly(avalon.maya.Creator):
name = "assembly"
label = "Assembly"
family = "assembly"
icon = "boxes"
icon = "cubes"
defaults = ['Main']

View file

@ -7,5 +7,5 @@ class CreateLayout(avalon.maya.Creator):
name = "layoutMain"
label = "Layout"
family = "layout"
icon = "boxes"
icon = "cubes"
defaults = ["Main"]

View file

@ -7,5 +7,5 @@ class CreateSetDress(avalon.maya.Creator):
name = "setdressMain"
label = "Set Dress"
family = "setdress"
icon = "boxes"
icon = "cubes"
defaults = ["Main", "Anim"]

View file

@ -45,7 +45,23 @@ class MayaAsciiLoader(pype.maya.plugin.ReferenceLoader):
cmds.setAttr(groupName + ".useOutlinerColor", 1)
cmds.setAttr(groupName + ".outlinerColor",
c[0], c[1], c[2])
cmds.setAttr(groupName + ".displayHandle", 1)
# get bounding box
bbox = cmds.exactWorldBoundingBox(groupName)
# get pivot position on world space
pivot = cmds.xform(groupName, q=True, sp=True, ws=True)
# center of bounding box
cx = (bbox[0] + bbox[3]) / 2
cy = (bbox[1] + bbox[4]) / 2
cz = (bbox[2] + bbox[5]) / 2
# add pivot position to calculate offset
cx = cx + pivot[0]
cy = cy + pivot[1]
cz = cz + pivot[2]
# set selection handle offset to center of bounding box
cmds.setAttr(groupName + ".selectHandleX", cx)
cmds.setAttr(groupName + ".selectHandleY", cy)
cmds.setAttr(groupName + ".selectHandleZ", cz)
return nodes
def switch(self, container, representation):

View file

@ -1,4 +1,4 @@
from avalon import api
import pype.maya.plugin
import os
from pypeapp import config
@ -58,6 +58,9 @@ class ReferenceLoader(pype.maya.plugin.ReferenceLoader):
for root in roots:
root.setParent(groupNode)
cmds.setAttr(groupName + ".displayHandle", 1)
groupNode
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
colors = presets['plugins']['maya']['load']['colors']
c = colors.get(family)
@ -67,11 +70,30 @@ class ReferenceLoader(pype.maya.plugin.ReferenceLoader):
self[:] = nodes
cmds.setAttr(groupName + ".displayHandle", 1)
# get bounding box
bbox = cmds.exactWorldBoundingBox(groupName)
# get pivot position on world space
pivot = cmds.xform(groupName, q=True, sp=True, ws=True)
# center of bounding box
cx = (bbox[0] + bbox[3]) / 2
cy = (bbox[1] + bbox[4]) / 2
cz = (bbox[2] + bbox[5]) / 2
# add pivot position to calculate offset
cx = cx + pivot[0]
cy = cy + pivot[1]
cz = cz + pivot[2]
# set selection handle offset to center of bounding box
cmds.setAttr(groupName + ".selectHandleX", cx)
cmds.setAttr(groupName + ".selectHandleY", cy)
cmds.setAttr(groupName + ".selectHandleZ", cz)
return nodes
def switch(self, container, representation):
self.update(container, representation)
# for backwards compatibility
class AbcLoader(ReferenceLoader):
label = "Deprecated loader (don't use)"
@ -79,6 +101,7 @@ class AbcLoader(ReferenceLoader):
representations = ["abc"]
tool_names = []
# for backwards compatibility
class ModelLoader(ReferenceLoader):
label = "Deprecated loader (don't use)"

View file

@ -11,5 +11,4 @@ class CollectActiveViewer(pyblish.api.ContextPlugin):
hosts = ["nuke"]
def process(self, context):
context.data["ViewerProcess"] = nuke.ViewerProcess.node()
context.data["ActiveViewer"] = nuke.activeViewer()

View file

@ -16,3 +16,9 @@ class ValidateActiveViewer(pyblish.api.ContextPlugin):
assert viewer_process_node, (
"Missing active viewer process! Please click on output write node and push key number 1-9"
)
active_viewer = context.data["ActiveViewer"]
active_input = active_viewer.activeInput()
assert active_input is not None, (
"Missing active viewer input! Please click on output write node and push key number 1-9"
)

View file

@ -14,6 +14,7 @@ class LoadLuts(api.Loader):
order = 0
icon = "cc"
color = style.colors.light
ignore_attr = ["useLifetime"]
def load(self, context, name, namespace, data):
"""
@ -83,6 +84,8 @@ class LoadLuts(api.Loader):
for ef_name, ef_val in nodes_order.items():
node = nuke.createNode(ef_val["class"])
for k, v in ef_val["node"].items():
if k in self.ignore_attr:
continue
if isinstance(v, list) and len(v) > 4:
node[k].setAnimated()
for i, value in enumerate(v):
@ -194,6 +197,8 @@ class LoadLuts(api.Loader):
for ef_name, ef_val in nodes_order.items():
node = nuke.createNode(ef_val["class"])
for k, v in ef_val["node"].items():
if k in self.ignore_attr:
continue
if isinstance(v, list) and len(v) > 3:
node[k].setAnimated()
for i, value in enumerate(v):

View file

@ -14,6 +14,7 @@ class LoadLutsInputProcess(api.Loader):
order = 0
icon = "eye"
color = style.colors.alert
ignore_attr = ["useLifetime"]
def load(self, context, name, namespace, data):
"""
@ -83,6 +84,8 @@ class LoadLutsInputProcess(api.Loader):
for ef_name, ef_val in nodes_order.items():
node = nuke.createNode(ef_val["class"])
for k, v in ef_val["node"].items():
if k in self.ignore_attr:
continue
if isinstance(v, list) and len(v) > 4:
node[k].setAnimated()
for i, value in enumerate(v):
@ -196,6 +199,8 @@ class LoadLutsInputProcess(api.Loader):
for ef_name, ef_val in nodes_order.items():
node = nuke.createNode(ef_val["class"])
for k, v in ef_val["node"].items():
if k in self.ignore_attr:
continue
if isinstance(v, list) and len(v) > 3:
node[k].setAnimated()
for i, value in enumerate(v):

View file

@ -0,0 +1,25 @@
import nuke
import pyblish.api
class CollectWriteLegacy(pyblish.api.ContextPlugin):
"""Collect legacy write nodes."""
order = pyblish.api.CollectorOrder
label = "Collect Write Legacy"
hosts = ["nuke", "nukeassist"]
def process(self, context):
for node in nuke.allNodes():
if node.Class() != "Write":
continue
if "avalon" not in node.knobs().keys():
continue
instance = context.create_instance(
node.name(), family="write.legacy"
)
instance.append(node)

View file

@ -15,21 +15,17 @@ class CreateOutputNode(pyblish.api.ContextPlugin):
def process(self, context):
# capture selection state
with maintained_selection():
# deselect all allNodes
self.log.info(context.data["ActiveViewer"])
active_node = [node for inst in context[:]
for node in inst[:]
if "ak:family" in node.knobs()]
active_viewer = context.data["ActiveViewer"]
active_input = active_viewer.activeInput()
active_node = active_viewer.node()
last_viewer_node = active_node.input(active_input)
name = last_viewer_node.name()
self.log.info("Node name: {}".format(name))
if active_node:
self.log.info(active_node)
active_node = active_node[0]
self.log.info(active_node)
active_node['selected'].setValue(True)
# select only instance render node
last_viewer_node['selected'].setValue(True)
output_node = nuke.createNode("Output")
# deselect all and select the original selection

View file

@ -3,7 +3,6 @@ import nuke
import pyblish.api
import pype
class ExtractReviewData(pype.api.Extractor):
"""Extracts movie and thumbnail with baked in luts
@ -48,9 +47,9 @@ class ExtractReviewData(pype.api.Extractor):
assert instance.data['representations'][0]['files'], "Instance data files should't be empty!"
import nuke
temporary_nodes = []
stagingDir = instance.data['representations'][0]["stagingDir"].replace("\\", "/")
stagingDir = instance.data[
'representations'][0]["stagingDir"].replace("\\", "/")
self.log.debug("StagingDir `{0}`...".format(stagingDir))
collection = instance.data.get("collection", None)
@ -70,16 +69,24 @@ class ExtractReviewData(pype.api.Extractor):
first_frame = instance.data.get("frameStart", None)
last_frame = instance.data.get("frameEnd", None)
node = previous_node = nuke.createNode("Read")
rnode = nuke.createNode("Read")
node["file"].setValue(
rnode["file"].setValue(
os.path.join(stagingDir, fname).replace("\\", "/"))
node["first"].setValue(first_frame)
node["origfirst"].setValue(first_frame)
node["last"].setValue(last_frame)
node["origlast"].setValue(last_frame)
temporary_nodes.append(node)
rnode["first"].setValue(first_frame)
rnode["origfirst"].setValue(first_frame)
rnode["last"].setValue(last_frame)
rnode["origlast"].setValue(last_frame)
temporary_nodes.append(rnode)
previous_node = rnode
# get input process and connect it to baking
ipn = self.get_view_process_node()
if ipn is not None:
ipn.setInput(0, previous_node)
previous_node = ipn
temporary_nodes.append(ipn)
reformat_node = nuke.createNode("Reformat")
@ -95,22 +102,10 @@ class ExtractReviewData(pype.api.Extractor):
previous_node = reformat_node
temporary_nodes.append(reformat_node)
viewer_process_node = instance.context.data.get("ViewerProcess")
dag_node = None
if viewer_process_node:
dag_node = nuke.createNode(viewer_process_node.Class())
dag_node.setInput(0, previous_node)
previous_node = dag_node
temporary_nodes.append(dag_node)
# Copy viewer process values
excludedKnobs = ["name", "xpos", "ypos"]
for item in viewer_process_node.knobs().keys():
if item not in excludedKnobs and item in dag_node.knobs():
x1 = viewer_process_node[item]
x2 = dag_node[item]
x2.fromScript(x1.toScript(False))
else:
self.log.warning("No viewer node found.")
dag_node = nuke.createNode("OCIODisplay")
dag_node.setInput(0, previous_node)
previous_node = dag_node
temporary_nodes.append(dag_node)
# create write node
write_node = nuke.createNode("Write")
@ -164,3 +159,28 @@ class ExtractReviewData(pype.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()]
for v in [n for n in nuke.allNodes()
if "Viewer" in n.Class()]:
ip = v['input_process'].getValue()
ipn = v['input_process_node'].getValue()
if "VIEWER_INPUT" not in ipn and ip:
ipn_orig = nuke.toNode(ipn)
ipn_orig.setSelected(True)
if ipn_orig:
nuke.nodeCopy('%clipboard%')
[n.setSelected(False) for n in nuke.selectedNodes()] # Deselect all
nuke.nodePaste('%clipboard%')
ipn = nuke.selectedNode()
return ipn

View file

@ -81,3 +81,5 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin):
).format(__name__)
instance.data['collection'] = collection
return

View file

@ -0,0 +1,62 @@
import toml
import os
import nuke
from avalon import api
import pyblish.api
class RepairWriteLegacyAction(pyblish.api.Action):
label = "Repair"
icon = "wrench"
on = "failed"
def process(self, context, plugin):
# Get the errored instances
failed = []
for result in context.data["results"]:
if (result["error"] is not None and result["instance"] is not None
and result["instance"] not in failed):
failed.append(result["instance"])
# Apply pyblish.logic to get the instances for the plug-in
instances = pyblish.api.instances_by_plugin(failed, plugin)
for instance in instances:
data = toml.loads(instance[0]["avalon"].value())
data["xpos"] = instance[0].xpos()
data["ypos"] = instance[0].ypos()
data["input"] = instance[0].input(0)
data["publish"] = instance[0]["publish"].value()
data["render"] = instance[0]["render"].value()
data["render_farm"] = instance[0]["render_farm"].value()
nuke.delete(instance[0])
family = "render{}".format(os.environ["AVALON_TASK"].capitalize())
api.create(data["subset"], data["asset"], family)
node = nuke.toNode(data["subset"])
node.setXYpos(data["xpos"], data["ypos"])
node.setInput(0, data["input"])
node["publish"].setValue(data["publish"])
node["render"].setValue(data["render"])
node["render_farm"].setValue(data["render_farm"])
class ValidateWriteLegacy(pyblish.api.InstancePlugin):
"""Validate legacy write nodes."""
order = pyblish.api.ValidatorOrder
optional = True
families = ["write.legacy"]
label = "Write Legacy"
hosts = ["nuke"]
actions = [RepairWriteLegacyAction]
def process(self, instance):
msg = "Clean up legacy write node \"{}\"".format(instance)
assert False, msg

View file

@ -14,7 +14,11 @@ class CollectVideoTracksLuts(pyblish.api.InstancePlugin):
self.log.debug("Finding soft effect for subset: `{}`".format(instance.data.get("subset")))
# taking active sequence
subset = instance.data["subset"]
subset = instance.data.get("subset")
if not subset:
return
track_effects = instance.context.data.get("trackEffects", {})
track_index = instance.data["trackIndex"]
effects = instance.data["effects"]
@ -74,7 +78,7 @@ class CollectVideoTracksLuts(pyblish.api.InstancePlugin):
'channels', 'maskChannelMask', 'maskChannelInput',
'note_font', 'note_font_size', 'unpremult',
'postage_stamp_frame', 'maskChannel', 'export_cc',
'select_cccid', 'mix', 'version']
'select_cccid', 'mix', 'version', 'matrix']
# loop trough all knobs and collect not ignored
# and any with any value

View file

@ -175,6 +175,8 @@ class CollectPlatesData(api.InstancePlugin):
if os.path.exists(mov_path):
# adding mov into the representations
self.log.debug("__ mov_path: {}".format(mov_path))
instance.data["label"] += " - review"
plates_mov_representation = {
'files': mov_file,
'stagingDir': staging_dir,

View file

@ -103,7 +103,7 @@ def avalon_api_publish(data, gui=True):
"-pp", os.pathsep.join(pyblish.api.registered_paths())
]
os.environ["PYBLISH_HOSTS"] = "shell"
os.environ["PYBLISH_HOSTS"] = "standalonepublisher"
os.environ["SAPUBLISH_INPATH"] = json_data_path
if gui:
@ -139,7 +139,7 @@ def cli_publish(data, gui=True):
if gui:
args += ["gui"]
os.environ["PYBLISH_HOSTS"] = "shell"
os.environ["PYBLISH_HOSTS"] = "standalonepublisher"
os.environ["SAPUBLISH_INPATH"] = json_data_path
os.environ["SAPUBLISH_OUTPATH"] = return_data_path

View file

@ -6,6 +6,7 @@ HelpRole = QtCore.Qt.UserRole + 2
FamilyRole = QtCore.Qt.UserRole + 3
ExistsRole = QtCore.Qt.UserRole + 4
PluginRole = QtCore.Qt.UserRole + 5
PluginKeyRole = QtCore.Qt.UserRole + 6
from ..resources import get_resource
from .button_from_svgs import SvgResizable, SvgButton

View file

@ -220,15 +220,21 @@ class DropDataFrame(QtWidgets.QFrame):
self._process_data(data)
def load_data_with_probe(self, filepath):
ffprobe_path = os.getenv("FFMPEG_PATH", "")
if ffprobe_path:
ffprobe_path += '/ffprobe'
else:
ffprobe_path = 'ffprobe'
args = [
'ffprobe',
ffprobe_path,
'-v', 'quiet',
'-print_format', 'json',
'-show_format',
'-show_streams', filepath
]
ffprobe_p = subprocess.Popen(
args,
' '.join(args),
stdout=subprocess.PIPE,
shell=True
)

View file

@ -5,7 +5,7 @@ import json
from collections import namedtuple
from . import QtWidgets, QtCore
from . import HelpRole, FamilyRole, ExistsRole, PluginRole
from . import HelpRole, FamilyRole, ExistsRole, PluginRole, PluginKeyRole
from . import FamilyDescriptionWidget
from pypeapp import config
@ -116,8 +116,10 @@ class FamilyWidget(QtWidgets.QWidget):
def collect_data(self):
plugin = self.list_families.currentItem().data(PluginRole)
key = self.list_families.currentItem().data(PluginKeyRole)
family = plugin.family.rsplit(".", 1)[-1]
data = {
'family_preset_key': key,
'family': family,
'subset': self.input_result.text(),
'version': self.version_spinbox.value()
@ -318,7 +320,7 @@ class FamilyWidget(QtWidgets.QWidget):
has_families = False
presets = config.get_presets().get('standalone_publish', {})
for creator in presets.get('families', {}).values():
for key, creator in presets.get('families', {}).items():
creator = namedtuple("Creator", creator.keys())(*creator.values())
label = creator.label or creator.family
@ -327,6 +329,7 @@ class FamilyWidget(QtWidgets.QWidget):
item.setData(HelpRole, creator.help or "")
item.setData(FamilyRole, creator.family)
item.setData(PluginRole, creator)
item.setData(PluginKeyRole, key)
item.setData(ExistsRole, False)
self.list_families.addItem(item)