mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into bugfix/PYPE-436-fix-collect-assumed-destination
This commit is contained in:
commit
66bf8b5bb9
30 changed files with 448 additions and 110 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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'))
|
||||
|
||||
|
|
|
|||
126
pype/plugins/global/publish/extract_thumbnail_sa.py
Normal file
126
pype/plugins/global/publish/extract_thumbnail_sa.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ class CreateAssembly(avalon.maya.Creator):
|
|||
name = "assembly"
|
||||
label = "Assembly"
|
||||
family = "assembly"
|
||||
icon = "boxes"
|
||||
icon = "cubes"
|
||||
defaults = ['Main']
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ class CreateLayout(avalon.maya.Creator):
|
|||
name = "layoutMain"
|
||||
label = "Layout"
|
||||
family = "layout"
|
||||
icon = "boxes"
|
||||
icon = "cubes"
|
||||
defaults = ["Main"]
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ class CreateSetDress(avalon.maya.Creator):
|
|||
name = "setdressMain"
|
||||
label = "Set Dress"
|
||||
family = "setdress"
|
||||
icon = "boxes"
|
||||
icon = "cubes"
|
||||
defaults = ["Main", "Anim"]
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
25
pype/plugins/nuke/publish/collect_legacy_write.py
Normal file
25
pype/plugins/nuke/publish/collect_legacy_write.py
Normal 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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -81,3 +81,5 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin):
|
|||
).format(__name__)
|
||||
|
||||
instance.data['collection'] = collection
|
||||
|
||||
return
|
||||
|
|
|
|||
62
pype/plugins/nuke/publish/validate_write_legacy.py
Normal file
62
pype/plugins/nuke/publish/validate_write_legacy.py
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue