mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge branch 'develop' into bugfix/nuke-intermediate-steams-bugs
This commit is contained in:
commit
633aa0ca7d
302 changed files with 1476 additions and 336 deletions
|
|
@ -51,8 +51,13 @@ IGNORED_MODULES_IN_AYON = set()
|
||||||
# - this is used to log the missing addon
|
# - this is used to log the missing addon
|
||||||
MOVED_ADDON_MILESTONE_VERSIONS = {
|
MOVED_ADDON_MILESTONE_VERSIONS = {
|
||||||
"applications": VersionInfo(0, 2, 0),
|
"applications": VersionInfo(0, 2, 0),
|
||||||
|
"clockify": VersionInfo(0, 2, 0),
|
||||||
|
"traypublisher": VersionInfo(0, 2, 0),
|
||||||
|
"tvpaint": VersionInfo(0, 2, 0),
|
||||||
|
"nuke": VersionInfo(0, 2, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Inherit from `object` for Python 2 hosts
|
# Inherit from `object` for Python 2 hosts
|
||||||
class _ModuleClass(object):
|
class _ModuleClass(object):
|
||||||
"""Fake module class for storing AYON addons.
|
"""Fake module class for storing AYON addons.
|
||||||
|
|
|
||||||
|
|
@ -365,3 +365,62 @@ def maintained_time():
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
bpy.context.scene.frame_current = current_time
|
bpy.context.scene.frame_current = current_time
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_parents(obj):
|
||||||
|
"""Get all recursive parents of object.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
obj (bpy.types.Object): Object to get all parents for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[bpy.types.Object]: All parents of object
|
||||||
|
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
while True:
|
||||||
|
obj = obj.parent
|
||||||
|
if not obj:
|
||||||
|
break
|
||||||
|
result.append(obj)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_highest_root(objects):
|
||||||
|
"""Get the highest object (the least parents) among the objects.
|
||||||
|
|
||||||
|
If multiple objects have the same amount of parents (or no parents) the
|
||||||
|
first object found in the input iterable will be returned.
|
||||||
|
|
||||||
|
Note that this will *not* return objects outside of the input list, as
|
||||||
|
such it will not return the root of node from a child node. It is purely
|
||||||
|
intended to find the highest object among a list of objects. To instead
|
||||||
|
get the root from one object use, e.g. `get_all_parents(obj)[-1]`
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
objects (List[bpy.types.Object]): Objects to find the highest root in.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[bpy.types.Object]: First highest root found or None if no
|
||||||
|
`bpy.types.Object` found in input list.
|
||||||
|
|
||||||
|
"""
|
||||||
|
included_objects = {obj.name_full for obj in objects}
|
||||||
|
num_parents_to_obj = {}
|
||||||
|
for obj in objects:
|
||||||
|
if isinstance(obj, bpy.types.Object):
|
||||||
|
parents = get_all_parents(obj)
|
||||||
|
# included parents
|
||||||
|
parents = [parent for parent in parents if
|
||||||
|
parent.name_full in included_objects]
|
||||||
|
if not parents:
|
||||||
|
# A node without parents must be a highest root
|
||||||
|
return obj
|
||||||
|
|
||||||
|
num_parents_to_obj.setdefault(len(parents), obj)
|
||||||
|
|
||||||
|
if not num_parents_to_obj:
|
||||||
|
return
|
||||||
|
|
||||||
|
minimum_parent = min(num_parents_to_obj)
|
||||||
|
return num_parents_to_obj[minimum_parent]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ from .ops import (
|
||||||
)
|
)
|
||||||
from .lib import imprint
|
from .lib import imprint
|
||||||
|
|
||||||
VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx"]
|
VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx",
|
||||||
|
".usd", ".usdc", ".usda"]
|
||||||
|
|
||||||
|
|
||||||
def prepare_scene_name(
|
def prepare_scene_name(
|
||||||
|
|
|
||||||
30
client/ayon_core/hosts/blender/plugins/create/create_usd.py
Normal file
30
client/ayon_core/hosts/blender/plugins/create/create_usd.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""Create a USD Export."""
|
||||||
|
|
||||||
|
from ayon_core.hosts.blender.api import plugin, lib
|
||||||
|
|
||||||
|
|
||||||
|
class CreateUSD(plugin.BaseCreator):
|
||||||
|
"""Create USD Export"""
|
||||||
|
|
||||||
|
identifier = "io.openpype.creators.blender.usd"
|
||||||
|
name = "usdMain"
|
||||||
|
label = "USD"
|
||||||
|
product_type = "usd"
|
||||||
|
icon = "gears"
|
||||||
|
|
||||||
|
def create(
|
||||||
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
|
):
|
||||||
|
# Run parent create method
|
||||||
|
collection = super().create(
|
||||||
|
product_name, instance_data, pre_create_data
|
||||||
|
)
|
||||||
|
|
||||||
|
if pre_create_data.get("use_selection"):
|
||||||
|
objects = lib.get_selection()
|
||||||
|
for obj in objects:
|
||||||
|
collection.objects.link(obj)
|
||||||
|
if obj.type == 'EMPTY':
|
||||||
|
objects.extend(obj.children)
|
||||||
|
|
||||||
|
return collection
|
||||||
|
|
@ -26,10 +26,10 @@ class CacheModelLoader(plugin.AssetLoader):
|
||||||
Note:
|
Note:
|
||||||
At least for now it only supports Alembic files.
|
At least for now it only supports Alembic files.
|
||||||
"""
|
"""
|
||||||
product_types = {"model", "pointcache", "animation"}
|
product_types = {"model", "pointcache", "animation", "usd"}
|
||||||
representations = {"abc"}
|
representations = {"abc", "usd"}
|
||||||
|
|
||||||
label = "Load Alembic"
|
label = "Load Cache"
|
||||||
icon = "code-fork"
|
icon = "code-fork"
|
||||||
color = "orange"
|
color = "orange"
|
||||||
|
|
||||||
|
|
@ -53,10 +53,21 @@ class CacheModelLoader(plugin.AssetLoader):
|
||||||
plugin.deselect_all()
|
plugin.deselect_all()
|
||||||
|
|
||||||
relative = bpy.context.preferences.filepaths.use_relative_paths
|
relative = bpy.context.preferences.filepaths.use_relative_paths
|
||||||
bpy.ops.wm.alembic_import(
|
|
||||||
filepath=libpath,
|
if any(libpath.lower().endswith(ext)
|
||||||
relative_path=relative
|
for ext in [".usd", ".usda", ".usdc"]):
|
||||||
)
|
# USD
|
||||||
|
bpy.ops.wm.usd_import(
|
||||||
|
filepath=libpath,
|
||||||
|
relative_path=relative
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Alembic
|
||||||
|
bpy.ops.wm.alembic_import(
|
||||||
|
filepath=libpath,
|
||||||
|
relative_path=relative
|
||||||
|
)
|
||||||
|
|
||||||
imported = lib.get_selection()
|
imported = lib.get_selection()
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ class CollectBlenderInstanceData(pyblish.api.InstancePlugin):
|
||||||
order = pyblish.api.CollectorOrder
|
order = pyblish.api.CollectorOrder
|
||||||
hosts = ["blender"]
|
hosts = ["blender"]
|
||||||
families = ["model", "pointcache", "animation", "rig", "camera", "layout",
|
families = ["model", "pointcache", "animation", "rig", "camera", "layout",
|
||||||
"blendScene"]
|
"blendScene", "usd"]
|
||||||
label = "Collect Instance"
|
label = "Collect Instance"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from ayon_core.pipeline import publish
|
||||||
|
from ayon_core.hosts.blender.api import plugin, lib
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractUSD(publish.Extractor):
|
||||||
|
"""Extract as USD."""
|
||||||
|
|
||||||
|
label = "Extract USD"
|
||||||
|
hosts = ["blender"]
|
||||||
|
families = ["usd"]
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
|
||||||
|
# Ignore runtime instances (e.g. USD layers)
|
||||||
|
# TODO: This is better done via more specific `families`
|
||||||
|
if not instance.data.get("transientData", {}).get("instance_node"):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Define extract output file path
|
||||||
|
stagingdir = self.staging_dir(instance)
|
||||||
|
filename = f"{instance.name}.usd"
|
||||||
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
# Perform extraction
|
||||||
|
self.log.debug("Performing extraction..")
|
||||||
|
|
||||||
|
# Select all members to "export selected"
|
||||||
|
plugin.deselect_all()
|
||||||
|
|
||||||
|
selected = []
|
||||||
|
for obj in instance:
|
||||||
|
if isinstance(obj, bpy.types.Object):
|
||||||
|
obj.select_set(True)
|
||||||
|
selected.append(obj)
|
||||||
|
|
||||||
|
root = lib.get_highest_root(objects=instance[:])
|
||||||
|
if not root:
|
||||||
|
instance_node = instance.data["transientData"]["instance_node"]
|
||||||
|
raise publish.KnownPublishError(
|
||||||
|
f"No root object found in instance: {instance_node.name}"
|
||||||
|
)
|
||||||
|
self.log.debug(f"Exporting using active root: {root.name}")
|
||||||
|
|
||||||
|
context = plugin.create_blender_context(
|
||||||
|
active=root, selected=selected)
|
||||||
|
|
||||||
|
# Export USD
|
||||||
|
with bpy.context.temp_override(**context):
|
||||||
|
bpy.ops.wm.usd_export(
|
||||||
|
filepath=filepath,
|
||||||
|
selected_objects_only=True,
|
||||||
|
export_textures=False,
|
||||||
|
relative_paths=False,
|
||||||
|
export_animation=False,
|
||||||
|
export_hair=False,
|
||||||
|
export_uvmaps=True,
|
||||||
|
# TODO: add for new version of Blender (4+?)
|
||||||
|
# export_mesh_colors=True,
|
||||||
|
export_normals=True,
|
||||||
|
export_materials=True,
|
||||||
|
use_instancing=True
|
||||||
|
)
|
||||||
|
|
||||||
|
plugin.deselect_all()
|
||||||
|
|
||||||
|
# Add representation
|
||||||
|
representation = {
|
||||||
|
'name': 'usd',
|
||||||
|
'ext': 'usd',
|
||||||
|
'files': filename,
|
||||||
|
"stagingDir": stagingdir,
|
||||||
|
}
|
||||||
|
instance.data.setdefault("representations", []).append(representation)
|
||||||
|
self.log.debug("Extracted instance '%s' to: %s",
|
||||||
|
instance.name, representation)
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractModelUSD(ExtractUSD):
|
||||||
|
"""Extract model as USD."""
|
||||||
|
|
||||||
|
label = "Extract USD (Model)"
|
||||||
|
hosts = ["blender"]
|
||||||
|
families = ["model"]
|
||||||
|
|
||||||
|
# Driven by settings
|
||||||
|
optional = True
|
||||||
141
client/ayon_core/hosts/houdini/plugins/create/create_model.py
Normal file
141
client/ayon_core/hosts/houdini/plugins/create/create_model.py
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Creator plugin for creating Model product type.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Currently, This creator plugin is the same as 'create_pointcache.py'
|
||||||
|
But renaming the product type to 'model'.
|
||||||
|
|
||||||
|
It's purpose to support
|
||||||
|
Maya (load/publish model from maya to/from houdini).
|
||||||
|
|
||||||
|
It's considered to support multiple representations in the future.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ayon_core.hosts.houdini.api import plugin
|
||||||
|
from ayon_core.lib import BoolDef
|
||||||
|
|
||||||
|
import hou
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CreateModel(plugin.HoudiniCreator):
|
||||||
|
"""Create Model"""
|
||||||
|
identifier = "io.openpype.creators.houdini.model"
|
||||||
|
label = "Model"
|
||||||
|
product_type = "model"
|
||||||
|
icon = "cube"
|
||||||
|
|
||||||
|
def create(self, product_name, instance_data, pre_create_data):
|
||||||
|
instance_data.pop("active", None)
|
||||||
|
instance_data.update({"node_type": "alembic"})
|
||||||
|
creator_attributes = instance_data.setdefault(
|
||||||
|
"creator_attributes", dict())
|
||||||
|
creator_attributes["farm"] = pre_create_data["farm"]
|
||||||
|
|
||||||
|
instance = super(CreateModel, self).create(
|
||||||
|
product_name,
|
||||||
|
instance_data,
|
||||||
|
pre_create_data)
|
||||||
|
|
||||||
|
instance_node = hou.node(instance.get("instance_node"))
|
||||||
|
parms = {
|
||||||
|
"use_sop_path": True,
|
||||||
|
"build_from_path": True,
|
||||||
|
"path_attrib": "path",
|
||||||
|
"prim_to_detail_pattern": "cbId",
|
||||||
|
"format": 2,
|
||||||
|
"facesets": 0,
|
||||||
|
"filename": hou.text.expandString(
|
||||||
|
"$HIP/pyblish/{}.abc".format(product_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.selected_nodes:
|
||||||
|
selected_node = self.selected_nodes[0]
|
||||||
|
|
||||||
|
# Although Houdini allows ObjNode path on `sop_path` for the
|
||||||
|
# the ROP node we prefer it set to the SopNode path explicitly
|
||||||
|
|
||||||
|
# Allow sop level paths (e.g. /obj/geo1/box1)
|
||||||
|
if isinstance(selected_node, hou.SopNode):
|
||||||
|
parms["sop_path"] = selected_node.path()
|
||||||
|
self.log.debug(
|
||||||
|
"Valid SopNode selection, 'SOP Path' in ROP will be set to '%s'."
|
||||||
|
% selected_node.path()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Allow object level paths to Geometry nodes (e.g. /obj/geo1)
|
||||||
|
# but do not allow other object level nodes types like cameras, etc.
|
||||||
|
elif isinstance(selected_node, hou.ObjNode) and \
|
||||||
|
selected_node.type().name() in ["geo"]:
|
||||||
|
|
||||||
|
# get the output node with the minimum
|
||||||
|
# 'outputidx' or the node with display flag
|
||||||
|
sop_path = self.get_obj_output(selected_node)
|
||||||
|
|
||||||
|
if sop_path:
|
||||||
|
parms["sop_path"] = sop_path.path()
|
||||||
|
self.log.debug(
|
||||||
|
"Valid ObjNode selection, 'SOP Path' in ROP will be set to "
|
||||||
|
"the child path '%s'."
|
||||||
|
% sop_path.path()
|
||||||
|
)
|
||||||
|
|
||||||
|
if not parms.get("sop_path", None):
|
||||||
|
self.log.debug(
|
||||||
|
"Selection isn't valid. 'SOP Path' in ROP will be empty."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.log.debug(
|
||||||
|
"No Selection. 'SOP Path' in ROP will be empty."
|
||||||
|
)
|
||||||
|
|
||||||
|
instance_node.setParms(parms)
|
||||||
|
instance_node.parm("trange").set(1)
|
||||||
|
|
||||||
|
# Explicitly set f1 and f2 to frame start.
|
||||||
|
# Which forces the rop node to export one frame.
|
||||||
|
instance_node.parmTuple('f').deleteAllKeyframes()
|
||||||
|
fstart = int(hou.hscriptExpression("$FSTART"))
|
||||||
|
instance_node.parmTuple('f').set((fstart, fstart, 1))
|
||||||
|
|
||||||
|
# Lock any parameters in this list
|
||||||
|
to_lock = ["prim_to_detail_pattern"]
|
||||||
|
self.lock_parameters(instance_node, to_lock)
|
||||||
|
|
||||||
|
def get_network_categories(self):
|
||||||
|
return [
|
||||||
|
hou.ropNodeTypeCategory(),
|
||||||
|
hou.sopNodeTypeCategory()
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_obj_output(self, obj_node):
|
||||||
|
"""Find output node with the smallest 'outputidx'."""
|
||||||
|
|
||||||
|
outputs = obj_node.subnetOutputs()
|
||||||
|
|
||||||
|
# if obj_node is empty
|
||||||
|
if not outputs:
|
||||||
|
return
|
||||||
|
|
||||||
|
# if obj_node has one output child whether its
|
||||||
|
# sop output node or a node with the render flag
|
||||||
|
elif len(outputs) == 1:
|
||||||
|
return outputs[0]
|
||||||
|
|
||||||
|
# if there are more than one, then it have multiple output nodes
|
||||||
|
# return the one with the minimum 'outputidx'
|
||||||
|
else:
|
||||||
|
return min(outputs,
|
||||||
|
key=lambda node: node.evalParm('outputidx'))
|
||||||
|
|
||||||
|
def get_instance_attr_defs(self):
|
||||||
|
return [
|
||||||
|
BoolDef("farm",
|
||||||
|
label="Submitting to Farm",
|
||||||
|
default=False)
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_pre_create_attr_defs(self):
|
||||||
|
attrs = super().get_pre_create_attr_defs()
|
||||||
|
# Use same attributes as for instance attributes
|
||||||
|
return attrs + self.get_instance_attr_defs()
|
||||||
|
|
@ -11,7 +11,7 @@ class CollectDataforCache(pyblish.api.InstancePlugin):
|
||||||
order = pyblish.api.CollectorOrder + 0.11
|
order = pyblish.api.CollectorOrder + 0.11
|
||||||
families = ["ass", "pointcache",
|
families = ["ass", "pointcache",
|
||||||
"mantraifd", "redshiftproxy",
|
"mantraifd", "redshiftproxy",
|
||||||
"vdbcache"]
|
"vdbcache", "model"]
|
||||||
hosts = ["houdini"]
|
hosts = ["houdini"]
|
||||||
targets = ["local", "remote"]
|
targets = ["local", "remote"]
|
||||||
label = "Collect Data for Cache"
|
label = "Collect Data for Cache"
|
||||||
|
|
@ -43,10 +43,7 @@ class CollectDataforCache(pyblish.api.InstancePlugin):
|
||||||
cache_files = {"_": instance.data["files"]}
|
cache_files = {"_": instance.data["files"]}
|
||||||
# Convert instance family to pointcache if it is bgeo or abc
|
# Convert instance family to pointcache if it is bgeo or abc
|
||||||
# because ???
|
# because ???
|
||||||
for family in instance.data["families"]:
|
self.log.debug(instance.data["families"])
|
||||||
if family == "bgeo" or "abc":
|
|
||||||
instance.data["productType"] = "pointcache"
|
|
||||||
break
|
|
||||||
instance.data.update({
|
instance.data.update({
|
||||||
"plugin": "Houdini",
|
"plugin": "Houdini",
|
||||||
"publish": True
|
"publish": True
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ class CollectChunkSize(pyblish.api.InstancePlugin,
|
||||||
order = pyblish.api.CollectorOrder + 0.05
|
order = pyblish.api.CollectorOrder + 0.05
|
||||||
families = ["ass", "pointcache",
|
families = ["ass", "pointcache",
|
||||||
"vdbcache", "mantraifd",
|
"vdbcache", "mantraifd",
|
||||||
"redshiftproxy"]
|
"redshiftproxy", "model"]
|
||||||
hosts = ["houdini"]
|
hosts = ["houdini"]
|
||||||
targets = ["local", "remote"]
|
targets = ["local", "remote"]
|
||||||
label = "Collect Chunk Size"
|
label = "Collect Chunk Size"
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,24 @@
|
||||||
"""Collector for pointcache types.
|
"""Collector for different types.
|
||||||
|
|
||||||
This will add additional family to pointcache instance based on
|
This will add additional families to different instance based on
|
||||||
the creator_identifier parameter.
|
the creator_identifier parameter.
|
||||||
"""
|
"""
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
class CollectPointcacheType(pyblish.api.InstancePlugin):
|
class CollectPointcacheType(pyblish.api.InstancePlugin):
|
||||||
"""Collect data type for pointcache instance."""
|
"""Collect data type for different instances."""
|
||||||
|
|
||||||
order = pyblish.api.CollectorOrder
|
order = pyblish.api.CollectorOrder
|
||||||
hosts = ["houdini"]
|
hosts = ["houdini"]
|
||||||
families = ["pointcache"]
|
families = ["pointcache", "model"]
|
||||||
label = "Collect type of pointcache"
|
label = "Collect instances types"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
if instance.data["creator_identifier"] == "io.openpype.creators.houdini.bgeo": # noqa: E501
|
if instance.data["creator_identifier"] == "io.openpype.creators.houdini.bgeo": # noqa: E501
|
||||||
instance.data["families"] += ["bgeo"]
|
instance.data["families"] += ["bgeo"]
|
||||||
elif instance.data["creator_identifier"] == "io.openpype.creators.houdini.pointcache": # noqa: E501
|
elif instance.data["creator_identifier"] in {
|
||||||
|
"io.openpype.creators.houdini.pointcache",
|
||||||
|
"io.openpype.creators.houdini.model"
|
||||||
|
}:
|
||||||
instance.data["families"] += ["abc"]
|
instance.data["families"] += ["abc"]
|
||||||
|
|
@ -132,6 +132,6 @@ class CollectLocalRenderInstances(pyblish.api.InstancePlugin):
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
# Remove original render instance
|
# Skip integrating original render instance.
|
||||||
# I can't remove it here as I still need it to trigger the render.
|
# We are not removing it because it's used to trigger the render.
|
||||||
# context.remove(instance)
|
instance.data["integrate"] = False
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ class CollectOutputSOPPath(pyblish.api.InstancePlugin):
|
||||||
"usd",
|
"usd",
|
||||||
"usdrender",
|
"usdrender",
|
||||||
"redshiftproxy",
|
"redshiftproxy",
|
||||||
"staticMesh"
|
"staticMesh",
|
||||||
|
"model"
|
||||||
]
|
]
|
||||||
|
|
||||||
hosts = ["houdini"]
|
hosts = ["houdini"]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Validator for checking that export is a single frame."""
|
||||||
|
import pyblish.api
|
||||||
|
from ayon_core.pipeline import (
|
||||||
|
PublishValidationError,
|
||||||
|
OptionalPyblishPluginMixin
|
||||||
|
)
|
||||||
|
from ayon_core.pipeline.publish import ValidateContentsOrder
|
||||||
|
from ayon_core.hosts.houdini.api.action import SelectInvalidAction
|
||||||
|
|
||||||
|
|
||||||
|
class ValidateSingleFrame(pyblish.api.InstancePlugin,
|
||||||
|
OptionalPyblishPluginMixin):
|
||||||
|
"""Validate Export is a Single Frame.
|
||||||
|
|
||||||
|
It checks if rop node is exporting one frame.
|
||||||
|
This is mainly for Model product type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
families = ["model"]
|
||||||
|
hosts = ["houdini"]
|
||||||
|
label = "Validate Single Frame"
|
||||||
|
order = ValidateContentsOrder + 0.1
|
||||||
|
actions = [SelectInvalidAction]
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
|
||||||
|
invalid = self.get_invalid(instance)
|
||||||
|
if invalid:
|
||||||
|
nodes = [n.path() for n in invalid]
|
||||||
|
raise PublishValidationError(
|
||||||
|
"See log for details. "
|
||||||
|
"Invalid nodes: {0}".format(nodes)
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_invalid(cls, instance):
|
||||||
|
|
||||||
|
invalid = []
|
||||||
|
|
||||||
|
frame_start = instance.data.get("frameStartHandle")
|
||||||
|
frame_end = instance.data.get("frameEndHandle")
|
||||||
|
|
||||||
|
# This happens if instance node has no 'trange' parameter.
|
||||||
|
if frame_start is None or frame_end is None:
|
||||||
|
cls.log.debug(
|
||||||
|
"No frame data, skipping check.."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if frame_start != frame_end:
|
||||||
|
invalid.append(instance.data["instance_node"])
|
||||||
|
cls.log.error(
|
||||||
|
"Invalid frame range on '%s'."
|
||||||
|
"You should use the same frame number for 'f1' "
|
||||||
|
"and 'f2' parameters.",
|
||||||
|
instance.data["instance_node"].path()
|
||||||
|
)
|
||||||
|
|
||||||
|
return invalid
|
||||||
|
|
@ -16,9 +16,13 @@ class ValidateMeshIsStatic(pyblish.api.InstancePlugin,
|
||||||
"""Validate mesh is static.
|
"""Validate mesh is static.
|
||||||
|
|
||||||
It checks if output node is time dependent.
|
It checks if output node is time dependent.
|
||||||
|
this avoids getting different output from ROP node when extracted
|
||||||
|
from a different frame than the first frame.
|
||||||
|
(Might be overly restrictive though)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
families = ["staticMesh"]
|
families = ["staticMesh",
|
||||||
|
"model"]
|
||||||
hosts = ["houdini"]
|
hosts = ["houdini"]
|
||||||
label = "Validate Mesh is Static"
|
label = "Validate Mesh is Static"
|
||||||
order = ValidateContentsOrder + 0.1
|
order = ValidateContentsOrder + 0.1
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ class ValidateIntermediateDirectoriesChecked(pyblish.api.InstancePlugin):
|
||||||
"""Validate Create Intermediate Directories is enabled on ROP node."""
|
"""Validate Create Intermediate Directories is enabled on ROP node."""
|
||||||
|
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
families = ["pointcache", "camera", "vdbcache"]
|
families = ["pointcache", "camera", "vdbcache", "model"]
|
||||||
hosts = ["houdini"]
|
hosts = ["houdini"]
|
||||||
label = "Create Intermediate Directories Checked"
|
label = "Create Intermediate Directories Checked"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class ValidateSopOutputNode(pyblish.api.InstancePlugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
families = ["pointcache", "vdbcache"]
|
families = ["pointcache", "vdbcache", "model"]
|
||||||
hosts = ["houdini"]
|
hosts = ["houdini"]
|
||||||
label = "Validate Output Node (SOP)"
|
label = "Validate Output Node (SOP)"
|
||||||
actions = [SelectROPAction, SelectInvalidAction]
|
actions = [SelectROPAction, SelectInvalidAction]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
from .clockify_module import ClockifyModule
|
|
||||||
|
|
||||||
__all__ = (
|
|
||||||
"ClockifyModule",
|
|
||||||
)
|
|
||||||
|
|
@ -26,27 +26,32 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin,
|
||||||
|
|
||||||
order = pyblish.api.CollectorOrder + 0.420
|
order = pyblish.api.CollectorOrder + 0.420
|
||||||
label = "Collect Deadline Pools"
|
label = "Collect Deadline Pools"
|
||||||
hosts = ["aftereffects",
|
hosts = [
|
||||||
"fusion",
|
"aftereffects",
|
||||||
"harmony"
|
"fusion",
|
||||||
"nuke",
|
"harmony",
|
||||||
"maya",
|
"maya",
|
||||||
"max",
|
"max",
|
||||||
"houdini"]
|
"houdini",
|
||||||
|
"nuke",
|
||||||
|
]
|
||||||
|
|
||||||
families = ["render",
|
families = [
|
||||||
"rendering",
|
"render",
|
||||||
"render.farm",
|
"prerender",
|
||||||
"renderFarm",
|
"rendering",
|
||||||
"renderlayer",
|
"render.farm",
|
||||||
"maxrender",
|
"renderFarm",
|
||||||
"usdrender",
|
"renderlayer",
|
||||||
"redshift_rop",
|
"maxrender",
|
||||||
"arnold_rop",
|
"usdrender",
|
||||||
"mantra_rop",
|
"redshift_rop",
|
||||||
"karma_rop",
|
"arnold_rop",
|
||||||
"vray_rop",
|
"mantra_rop",
|
||||||
"publish.hou"]
|
"karma_rop",
|
||||||
|
"vray_rop",
|
||||||
|
"publish.hou",
|
||||||
|
]
|
||||||
|
|
||||||
primary_pool = None
|
primary_pool = None
|
||||||
secondary_pool = None
|
secondary_pool = None
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
from ayon_core.pipeline.publish import PublishValidationError
|
from ayon_core.pipeline.publish import (
|
||||||
|
PublishValidationError, OptionalPyblishPluginMixin
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ValidateVersion(pyblish.api.InstancePlugin):
|
class ValidateVersion(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin):
|
||||||
"""Validate instance version.
|
"""Validate instance version.
|
||||||
|
|
||||||
AYON does not allow overwriting previously published versions.
|
AYON does not allow overwriting previously published versions.
|
||||||
|
|
@ -18,6 +20,9 @@ class ValidateVersion(pyblish.api.InstancePlugin):
|
||||||
active = True
|
active = True
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
|
if not self.is_active(instance.data):
|
||||||
|
return
|
||||||
|
|
||||||
version = instance.data.get("version")
|
version = instance.data.get("version")
|
||||||
latest_version = instance.data.get("latestVersion")
|
latest_version = instance.data.get("latestVersion")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import ayon_api
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ayon_core.style import get_default_entity_icon_color
|
from ayon_core.style import get_default_entity_icon_color
|
||||||
from ayon_core.lib import CacheItem
|
from ayon_core.lib import CacheItem, NestedCacheItem
|
||||||
|
|
||||||
PROJECTS_MODEL_SENDER = "projects.model"
|
PROJECTS_MODEL_SENDER = "projects.model"
|
||||||
|
|
||||||
|
|
@ -17,6 +17,49 @@ class AbstractHierarchyController:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class StatusItem:
|
||||||
|
"""Item representing status of project.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): Status name ("Not ready").
|
||||||
|
color (str): Status color in hex ("#434a56").
|
||||||
|
short (str): Short status name ("NRD").
|
||||||
|
icon (str): Icon name in MaterialIcons ("fiber_new").
|
||||||
|
state (Literal["not_started", "in_progress", "done", "blocked"]):
|
||||||
|
Status state.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, name, color, short, icon, state):
|
||||||
|
self.name = name
|
||||||
|
self.color = color
|
||||||
|
self.short = short
|
||||||
|
self.icon = icon
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
def to_data(self):
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"color": self.color,
|
||||||
|
"short": self.short,
|
||||||
|
"icon": self.icon,
|
||||||
|
"state": self.state,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_data(cls, data):
|
||||||
|
return cls(**data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_project_item(cls, status_data):
|
||||||
|
return cls(
|
||||||
|
name=status_data["name"],
|
||||||
|
color=status_data["color"],
|
||||||
|
short=status_data["shortName"],
|
||||||
|
icon=status_data["icon"],
|
||||||
|
state=status_data["state"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProjectItem:
|
class ProjectItem:
|
||||||
"""Item representing folder entity on a server.
|
"""Item representing folder entity on a server.
|
||||||
|
|
||||||
|
|
@ -40,6 +83,23 @@ class ProjectItem:
|
||||||
}
|
}
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_entity(cls, project_entity):
|
||||||
|
"""Creates folder item from entity.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_entity (dict[str, Any]): Project entity.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ProjectItem: Project item.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return cls(
|
||||||
|
project_entity["name"],
|
||||||
|
project_entity["active"],
|
||||||
|
project_entity["library"],
|
||||||
|
)
|
||||||
|
|
||||||
def to_data(self):
|
def to_data(self):
|
||||||
"""Converts folder item to data.
|
"""Converts folder item to data.
|
||||||
|
|
||||||
|
|
@ -79,7 +139,7 @@ def _get_project_items_from_entitiy(projects):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return [
|
return [
|
||||||
ProjectItem(project["name"], project["active"], project["library"])
|
ProjectItem.from_entity(project)
|
||||||
for project in projects
|
for project in projects
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -87,18 +147,29 @@ def _get_project_items_from_entitiy(projects):
|
||||||
class ProjectsModel(object):
|
class ProjectsModel(object):
|
||||||
def __init__(self, controller):
|
def __init__(self, controller):
|
||||||
self._projects_cache = CacheItem(default_factory=list)
|
self._projects_cache = CacheItem(default_factory=list)
|
||||||
self._project_items_by_name = {}
|
self._project_statuses_cache = NestedCacheItem(
|
||||||
self._projects_by_name = {}
|
levels=1, default_factory=list
|
||||||
|
)
|
||||||
|
self._projects_by_name = NestedCacheItem(
|
||||||
|
levels=1, default_factory=list
|
||||||
|
)
|
||||||
|
|
||||||
self._is_refreshing = False
|
self._is_refreshing = False
|
||||||
self._controller = controller
|
self._controller = controller
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._projects_cache.reset()
|
self._projects_cache.reset()
|
||||||
self._project_items_by_name = {}
|
self._project_statuses_cache.reset()
|
||||||
self._projects_by_name = {}
|
self._projects_by_name.reset()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
|
"""Refresh project items.
|
||||||
|
|
||||||
|
This method will requery list of ProjectItem returned by
|
||||||
|
'get_project_items'.
|
||||||
|
|
||||||
|
To reset all cached items use 'reset' method.
|
||||||
|
"""
|
||||||
self._refresh_projects_cache()
|
self._refresh_projects_cache()
|
||||||
|
|
||||||
def get_project_items(self, sender):
|
def get_project_items(self, sender):
|
||||||
|
|
@ -117,12 +188,51 @@ class ProjectsModel(object):
|
||||||
return self._projects_cache.get_data()
|
return self._projects_cache.get_data()
|
||||||
|
|
||||||
def get_project_entity(self, project_name):
|
def get_project_entity(self, project_name):
|
||||||
if project_name not in self._projects_by_name:
|
"""Get project entity.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_name (str): Project name.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Union[dict[str, Any], None]: Project entity or None if project
|
||||||
|
was not found by name.
|
||||||
|
|
||||||
|
"""
|
||||||
|
project_cache = self._projects_by_name[project_name]
|
||||||
|
if not project_cache.is_valid:
|
||||||
entity = None
|
entity = None
|
||||||
if project_name:
|
if project_name:
|
||||||
entity = ayon_api.get_project(project_name)
|
entity = ayon_api.get_project(project_name)
|
||||||
self._projects_by_name[project_name] = entity
|
project_cache.update_data(entity)
|
||||||
return self._projects_by_name[project_name]
|
return project_cache.get_data()
|
||||||
|
|
||||||
|
def get_project_status_items(self, project_name, sender):
|
||||||
|
"""Get project status items.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_name (str): Project name.
|
||||||
|
sender (Union[str, None]): Name of sender who asked for items.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[StatusItem]: Status items for project.
|
||||||
|
|
||||||
|
"""
|
||||||
|
statuses_cache = self._project_statuses_cache[project_name]
|
||||||
|
if not statuses_cache.is_valid:
|
||||||
|
with self._project_statuses_refresh_event_manager(
|
||||||
|
sender, project_name
|
||||||
|
):
|
||||||
|
project_entity = None
|
||||||
|
if project_name:
|
||||||
|
project_entity = self.get_project_entity(project_name)
|
||||||
|
statuses = []
|
||||||
|
if project_entity:
|
||||||
|
statuses = [
|
||||||
|
StatusItem.from_project_item(status)
|
||||||
|
for status in project_entity["statuses"]
|
||||||
|
]
|
||||||
|
statuses_cache.update_data(statuses)
|
||||||
|
return statuses_cache.get_data()
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _project_refresh_event_manager(self, sender):
|
def _project_refresh_event_manager(self, sender):
|
||||||
|
|
@ -143,6 +253,23 @@ class ProjectsModel(object):
|
||||||
)
|
)
|
||||||
self._is_refreshing = False
|
self._is_refreshing = False
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def _project_statuses_refresh_event_manager(self, sender, project_name):
|
||||||
|
self._controller.emit_event(
|
||||||
|
"projects.statuses.refresh.started",
|
||||||
|
{"sender": sender, "project_name": project_name},
|
||||||
|
PROJECTS_MODEL_SENDER
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self._controller.emit_event(
|
||||||
|
"projects.statuses.refresh.finished",
|
||||||
|
{"sender": sender, "project_name": project_name},
|
||||||
|
PROJECTS_MODEL_SENDER
|
||||||
|
)
|
||||||
|
|
||||||
def _refresh_projects_cache(self, sender=None):
|
def _refresh_projects_cache(self, sender=None):
|
||||||
if self._is_refreshing:
|
if self._is_refreshing:
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,34 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
painter.drawPixmap(extender_x, extender_y, pix)
|
painter.drawPixmap(extender_x, extender_y, pix)
|
||||||
|
|
||||||
|
|
||||||
|
class ActionsProxyModel(QtCore.QSortFilterProxyModel):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||||
|
|
||||||
|
def lessThan(self, left, right):
|
||||||
|
# Sort by action order and then by label
|
||||||
|
left_value = left.data(ACTION_SORT_ROLE)
|
||||||
|
right_value = right.data(ACTION_SORT_ROLE)
|
||||||
|
|
||||||
|
# Values are same -> use super sorting
|
||||||
|
if left_value == right_value:
|
||||||
|
# Default behavior is using DisplayRole
|
||||||
|
return super().lessThan(left, right)
|
||||||
|
|
||||||
|
# Validate 'None' values
|
||||||
|
if right_value is None:
|
||||||
|
return True
|
||||||
|
if left_value is None:
|
||||||
|
return False
|
||||||
|
# Sort values and handle incompatible types
|
||||||
|
try:
|
||||||
|
return left_value < right_value
|
||||||
|
except TypeError:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ActionsWidget(QtWidgets.QWidget):
|
class ActionsWidget(QtWidgets.QWidget):
|
||||||
def __init__(self, controller, parent):
|
def __init__(self, controller, parent):
|
||||||
super(ActionsWidget, self).__init__(parent)
|
super(ActionsWidget, self).__init__(parent)
|
||||||
|
|
@ -316,10 +344,7 @@ class ActionsWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
model = ActionsQtModel(controller)
|
model = ActionsQtModel(controller)
|
||||||
|
|
||||||
proxy_model = QtCore.QSortFilterProxyModel()
|
proxy_model = ActionsProxyModel()
|
||||||
proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
|
||||||
proxy_model.setSortRole(ACTION_SORT_ROLE)
|
|
||||||
|
|
||||||
proxy_model.setSourceModel(model)
|
proxy_model.setSourceModel(model)
|
||||||
view.setModel(proxy_model)
|
view.setModel(proxy_model)
|
||||||
|
|
||||||
|
|
@ -359,7 +384,8 @@ class ActionsWidget(QtWidgets.QWidget):
|
||||||
def _on_model_refresh(self):
|
def _on_model_refresh(self):
|
||||||
self._proxy_model.sort(0)
|
self._proxy_model.sort(0)
|
||||||
# Force repaint all items
|
# Force repaint all items
|
||||||
self._view.update()
|
viewport = self._view.viewport()
|
||||||
|
viewport.update()
|
||||||
|
|
||||||
def _on_animation(self):
|
def _on_animation(self):
|
||||||
time_now = time.time()
|
time_now = time.time()
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ class VersionItem:
|
||||||
thumbnail_id (Union[str, None]): Thumbnail id.
|
thumbnail_id (Union[str, None]): Thumbnail id.
|
||||||
published_time (Union[str, None]): Published time in format
|
published_time (Union[str, None]): Published time in format
|
||||||
'%Y%m%dT%H%M%SZ'.
|
'%Y%m%dT%H%M%SZ'.
|
||||||
|
status (Union[str, None]): Status name.
|
||||||
author (Union[str, None]): Author.
|
author (Union[str, None]): Author.
|
||||||
frame_range (Union[str, None]): Frame range.
|
frame_range (Union[str, None]): Frame range.
|
||||||
duration (Union[int, None]): Duration.
|
duration (Union[int, None]): Duration.
|
||||||
|
|
@ -132,6 +133,7 @@ class VersionItem:
|
||||||
thumbnail_id,
|
thumbnail_id,
|
||||||
published_time,
|
published_time,
|
||||||
author,
|
author,
|
||||||
|
status,
|
||||||
frame_range,
|
frame_range,
|
||||||
duration,
|
duration,
|
||||||
handles,
|
handles,
|
||||||
|
|
@ -146,6 +148,7 @@ class VersionItem:
|
||||||
self.is_hero = is_hero
|
self.is_hero = is_hero
|
||||||
self.published_time = published_time
|
self.published_time = published_time
|
||||||
self.author = author
|
self.author = author
|
||||||
|
self.status = status
|
||||||
self.frame_range = frame_range
|
self.frame_range = frame_range
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
self.handles = handles
|
self.handles = handles
|
||||||
|
|
@ -185,6 +188,7 @@ class VersionItem:
|
||||||
"is_hero": self.is_hero,
|
"is_hero": self.is_hero,
|
||||||
"published_time": self.published_time,
|
"published_time": self.published_time,
|
||||||
"author": self.author,
|
"author": self.author,
|
||||||
|
"status": self.status,
|
||||||
"frame_range": self.frame_range,
|
"frame_range": self.frame_range,
|
||||||
"duration": self.duration,
|
"duration": self.duration,
|
||||||
"handles": self.handles,
|
"handles": self.handles,
|
||||||
|
|
@ -488,6 +492,27 @@ class FrontendLoaderController(_BaseLoaderController):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_project_status_items(self, project_name, sender=None):
|
||||||
|
"""Items for all projects available on server.
|
||||||
|
|
||||||
|
Triggers event topics "projects.statuses.refresh.started" and
|
||||||
|
"projects.statuses.refresh.finished" with data:
|
||||||
|
{
|
||||||
|
"sender": sender,
|
||||||
|
"project_name": project_name
|
||||||
|
}
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_name (Union[str, None]): Project name.
|
||||||
|
sender (Optional[str]): Sender who requested the items.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[StatusItem]: List of status items.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_product_items(self, project_name, folder_ids, sender=None):
|
def get_product_items(self, project_name, folder_ids, sender=None):
|
||||||
"""Product items for folder ids.
|
"""Product items for folder ids.
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,11 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
|
||||||
def get_project_items(self, sender=None):
|
def get_project_items(self, sender=None):
|
||||||
return self._projects_model.get_project_items(sender)
|
return self._projects_model.get_project_items(sender)
|
||||||
|
|
||||||
|
def get_project_status_items(self, project_name, sender=None):
|
||||||
|
return self._projects_model.get_project_status_items(
|
||||||
|
project_name, sender
|
||||||
|
)
|
||||||
|
|
||||||
def get_folder_items(self, project_name, sender=None):
|
def get_folder_items(self, project_name, sender=None):
|
||||||
return self._hierarchy_model.get_folder_items(project_name, sender)
|
return self._hierarchy_model.get_folder_items(project_name, sender)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ def version_item_from_entity(version):
|
||||||
thumbnail_id=version["thumbnailId"],
|
thumbnail_id=version["thumbnailId"],
|
||||||
published_time=published_time,
|
published_time=published_time,
|
||||||
author=author,
|
author=author,
|
||||||
|
status=version["status"],
|
||||||
frame_range=frame_range,
|
frame_range=frame_range,
|
||||||
duration=duration,
|
duration=duration,
|
||||||
handles=handles,
|
handles=handles,
|
||||||
|
|
@ -526,8 +527,11 @@ class ProductsModel:
|
||||||
products = list(ayon_api.get_products(project_name, **kwargs))
|
products = list(ayon_api.get_products(project_name, **kwargs))
|
||||||
product_ids = {product["id"] for product in products}
|
product_ids = {product["id"] for product in products}
|
||||||
|
|
||||||
|
# Add 'status' to fields -> fixed in ayon-python-api 1.0.4
|
||||||
|
fields = ayon_api.get_default_fields_for_type("version")
|
||||||
|
fields.add("status")
|
||||||
versions = ayon_api.get_versions(
|
versions = ayon_api.get_versions(
|
||||||
project_name, product_ids=product_ids
|
project_name, product_ids=product_ids, fields=fields
|
||||||
)
|
)
|
||||||
|
|
||||||
return self._create_product_items(
|
return self._create_product_items(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ from ayon_core.tools.utils.lib import format_version
|
||||||
from .products_model import (
|
from .products_model import (
|
||||||
PRODUCT_ID_ROLE,
|
PRODUCT_ID_ROLE,
|
||||||
VERSION_NAME_EDIT_ROLE,
|
VERSION_NAME_EDIT_ROLE,
|
||||||
|
VERSION_STATUS_NAME_ROLE,
|
||||||
|
VERSION_STATUS_SHORT_ROLE,
|
||||||
|
VERSION_STATUS_COLOR_ROLE,
|
||||||
VERSION_ID_ROLE,
|
VERSION_ID_ROLE,
|
||||||
PRODUCT_IN_SCENE_ROLE,
|
PRODUCT_IN_SCENE_ROLE,
|
||||||
ACTIVE_SITE_ICON_ROLE,
|
ACTIVE_SITE_ICON_ROLE,
|
||||||
|
|
@ -104,7 +107,10 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
style = QtWidgets.QApplication.style()
|
style = QtWidgets.QApplication.style()
|
||||||
|
|
||||||
style.drawControl(
|
style.drawControl(
|
||||||
style.CE_ItemViewItem, option, painter, option.widget
|
QtWidgets.QCommonStyle.CE_ItemViewItem,
|
||||||
|
option,
|
||||||
|
painter,
|
||||||
|
option.widget
|
||||||
)
|
)
|
||||||
|
|
||||||
painter.save()
|
painter.save()
|
||||||
|
|
@ -116,9 +122,14 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
pen.setColor(fg_color)
|
pen.setColor(fg_color)
|
||||||
painter.setPen(pen)
|
painter.setPen(pen)
|
||||||
|
|
||||||
text_rect = style.subElementRect(style.SE_ItemViewItemText, option)
|
text_rect = style.subElementRect(
|
||||||
|
QtWidgets.QCommonStyle.SE_ItemViewItemText,
|
||||||
|
option
|
||||||
|
)
|
||||||
text_margin = style.proxy().pixelMetric(
|
text_margin = style.proxy().pixelMetric(
|
||||||
style.PM_FocusFrameHMargin, option, option.widget
|
QtWidgets.QCommonStyle.PM_FocusFrameHMargin,
|
||||||
|
option,
|
||||||
|
option.widget
|
||||||
) + 1
|
) + 1
|
||||||
|
|
||||||
painter.drawText(
|
painter.drawText(
|
||||||
|
|
@ -194,6 +205,57 @@ class LoadedInSceneDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
option.palette.setBrush(QtGui.QPalette.Text, color)
|
option.palette.setBrush(QtGui.QPalette.Text, color)
|
||||||
|
|
||||||
|
|
||||||
|
class StatusDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
|
"""Delegate showing status name and short name."""
|
||||||
|
|
||||||
|
def paint(self, painter, option, index):
|
||||||
|
if option.widget:
|
||||||
|
style = option.widget.style()
|
||||||
|
else:
|
||||||
|
style = QtWidgets.QApplication.style()
|
||||||
|
|
||||||
|
style.drawControl(
|
||||||
|
QtWidgets.QCommonStyle.CE_ItemViewItem,
|
||||||
|
option,
|
||||||
|
painter,
|
||||||
|
option.widget
|
||||||
|
)
|
||||||
|
|
||||||
|
painter.save()
|
||||||
|
|
||||||
|
text_rect = style.subElementRect(
|
||||||
|
QtWidgets.QCommonStyle.SE_ItemViewItemText,
|
||||||
|
option
|
||||||
|
)
|
||||||
|
text_margin = style.proxy().pixelMetric(
|
||||||
|
QtWidgets.QCommonStyle.PM_FocusFrameHMargin,
|
||||||
|
option,
|
||||||
|
option.widget
|
||||||
|
) + 1
|
||||||
|
padded_text_rect = text_rect.adjusted(
|
||||||
|
text_margin, 0, - text_margin, 0
|
||||||
|
)
|
||||||
|
|
||||||
|
fm = QtGui.QFontMetrics(option.font)
|
||||||
|
text = index.data(VERSION_STATUS_NAME_ROLE)
|
||||||
|
if padded_text_rect.width() < fm.width(text):
|
||||||
|
text = index.data(VERSION_STATUS_SHORT_ROLE)
|
||||||
|
|
||||||
|
status_color = index.data(VERSION_STATUS_COLOR_ROLE)
|
||||||
|
fg_color = QtGui.QColor(status_color)
|
||||||
|
pen = painter.pen()
|
||||||
|
pen.setColor(fg_color)
|
||||||
|
painter.setPen(pen)
|
||||||
|
|
||||||
|
painter.drawText(
|
||||||
|
padded_text_rect,
|
||||||
|
option.displayAlignment,
|
||||||
|
text
|
||||||
|
)
|
||||||
|
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
|
||||||
class SiteSyncDelegate(QtWidgets.QStyledItemDelegate):
|
class SiteSyncDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
"""Paints icons and downloaded representation ration for both sites."""
|
"""Paints icons and downloaded representation ration for both sites."""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,21 @@ VERSION_HERO_ROLE = QtCore.Qt.UserRole + 11
|
||||||
VERSION_NAME_ROLE = QtCore.Qt.UserRole + 12
|
VERSION_NAME_ROLE = QtCore.Qt.UserRole + 12
|
||||||
VERSION_NAME_EDIT_ROLE = QtCore.Qt.UserRole + 13
|
VERSION_NAME_EDIT_ROLE = QtCore.Qt.UserRole + 13
|
||||||
VERSION_PUBLISH_TIME_ROLE = QtCore.Qt.UserRole + 14
|
VERSION_PUBLISH_TIME_ROLE = QtCore.Qt.UserRole + 14
|
||||||
VERSION_AUTHOR_ROLE = QtCore.Qt.UserRole + 15
|
VERSION_STATUS_NAME_ROLE = QtCore.Qt.UserRole + 15
|
||||||
VERSION_FRAME_RANGE_ROLE = QtCore.Qt.UserRole + 16
|
VERSION_STATUS_SHORT_ROLE = QtCore.Qt.UserRole + 16
|
||||||
VERSION_DURATION_ROLE = QtCore.Qt.UserRole + 17
|
VERSION_STATUS_COLOR_ROLE = QtCore.Qt.UserRole + 17
|
||||||
VERSION_HANDLES_ROLE = QtCore.Qt.UserRole + 18
|
VERSION_AUTHOR_ROLE = QtCore.Qt.UserRole + 18
|
||||||
VERSION_STEP_ROLE = QtCore.Qt.UserRole + 19
|
VERSION_FRAME_RANGE_ROLE = QtCore.Qt.UserRole + 19
|
||||||
VERSION_AVAILABLE_ROLE = QtCore.Qt.UserRole + 20
|
VERSION_DURATION_ROLE = QtCore.Qt.UserRole + 20
|
||||||
VERSION_THUMBNAIL_ID_ROLE = QtCore.Qt.UserRole + 21
|
VERSION_HANDLES_ROLE = QtCore.Qt.UserRole + 21
|
||||||
ACTIVE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 22
|
VERSION_STEP_ROLE = QtCore.Qt.UserRole + 22
|
||||||
REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 23
|
VERSION_AVAILABLE_ROLE = QtCore.Qt.UserRole + 23
|
||||||
REPRESENTATIONS_COUNT_ROLE = QtCore.Qt.UserRole + 24
|
VERSION_THUMBNAIL_ID_ROLE = QtCore.Qt.UserRole + 24
|
||||||
SYNC_ACTIVE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 25
|
ACTIVE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 25
|
||||||
SYNC_REMOTE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 26
|
REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 26
|
||||||
|
REPRESENTATIONS_COUNT_ROLE = QtCore.Qt.UserRole + 27
|
||||||
|
SYNC_ACTIVE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 28
|
||||||
|
SYNC_REMOTE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 29
|
||||||
|
|
||||||
|
|
||||||
class ProductsModel(QtGui.QStandardItemModel):
|
class ProductsModel(QtGui.QStandardItemModel):
|
||||||
|
|
@ -44,6 +47,7 @@ class ProductsModel(QtGui.QStandardItemModel):
|
||||||
"Product type",
|
"Product type",
|
||||||
"Folder",
|
"Folder",
|
||||||
"Version",
|
"Version",
|
||||||
|
"Status",
|
||||||
"Time",
|
"Time",
|
||||||
"Author",
|
"Author",
|
||||||
"Frames",
|
"Frames",
|
||||||
|
|
@ -69,11 +73,35 @@ class ProductsModel(QtGui.QStandardItemModel):
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
version_col = column_labels.index("Version")
|
product_name_col = column_labels.index("Product name")
|
||||||
published_time_col = column_labels.index("Time")
|
product_type_col = column_labels.index("Product type")
|
||||||
folders_label_col = column_labels.index("Folder")
|
folders_label_col = column_labels.index("Folder")
|
||||||
|
version_col = column_labels.index("Version")
|
||||||
|
status_col = column_labels.index("Status")
|
||||||
|
published_time_col = column_labels.index("Time")
|
||||||
|
author_col = column_labels.index("Author")
|
||||||
|
frame_range_col = column_labels.index("Frames")
|
||||||
|
duration_col = column_labels.index("Duration")
|
||||||
|
handles_col = column_labels.index("Handles")
|
||||||
|
step_col = column_labels.index("Step")
|
||||||
in_scene_col = column_labels.index("In scene")
|
in_scene_col = column_labels.index("In scene")
|
||||||
sitesync_avail_col = column_labels.index("Availability")
|
sitesync_avail_col = column_labels.index("Availability")
|
||||||
|
_display_role_mapping = {
|
||||||
|
product_name_col: QtCore.Qt.DisplayRole,
|
||||||
|
product_type_col: PRODUCT_TYPE_ROLE,
|
||||||
|
folders_label_col: FOLDER_LABEL_ROLE,
|
||||||
|
version_col: VERSION_NAME_ROLE,
|
||||||
|
status_col: VERSION_STATUS_NAME_ROLE,
|
||||||
|
published_time_col: VERSION_PUBLISH_TIME_ROLE,
|
||||||
|
author_col: VERSION_AUTHOR_ROLE,
|
||||||
|
frame_range_col: VERSION_FRAME_RANGE_ROLE,
|
||||||
|
duration_col: VERSION_DURATION_ROLE,
|
||||||
|
handles_col: VERSION_HANDLES_ROLE,
|
||||||
|
step_col: VERSION_STEP_ROLE,
|
||||||
|
in_scene_col: PRODUCT_IN_SCENE_ROLE,
|
||||||
|
sitesync_avail_col: VERSION_AVAILABLE_ROLE,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, controller):
|
def __init__(self, controller):
|
||||||
super(ProductsModel, self).__init__()
|
super(ProductsModel, self).__init__()
|
||||||
|
|
@ -96,6 +124,7 @@ class ProductsModel(QtGui.QStandardItemModel):
|
||||||
|
|
||||||
self._last_project_name = None
|
self._last_project_name = None
|
||||||
self._last_folder_ids = []
|
self._last_folder_ids = []
|
||||||
|
self._last_project_statuses = {}
|
||||||
|
|
||||||
def get_product_item_indexes(self):
|
def get_product_item_indexes(self):
|
||||||
return [
|
return [
|
||||||
|
|
@ -141,6 +170,15 @@ class ProductsModel(QtGui.QStandardItemModel):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if role in (VERSION_STATUS_SHORT_ROLE, VERSION_STATUS_COLOR_ROLE):
|
||||||
|
status_name = self.data(index, VERSION_STATUS_NAME_ROLE)
|
||||||
|
status_item = self._last_project_statuses.get(status_name)
|
||||||
|
if status_item is None:
|
||||||
|
return ""
|
||||||
|
if role == VERSION_STATUS_SHORT_ROLE:
|
||||||
|
return status_item.short
|
||||||
|
return status_item.color
|
||||||
|
|
||||||
col = index.column()
|
col = index.column()
|
||||||
if col == 0:
|
if col == 0:
|
||||||
return super(ProductsModel, self).data(index, role)
|
return super(ProductsModel, self).data(index, role)
|
||||||
|
|
@ -168,29 +206,8 @@ class ProductsModel(QtGui.QStandardItemModel):
|
||||||
if role == QtCore.Qt.DisplayRole:
|
if role == QtCore.Qt.DisplayRole:
|
||||||
if not index.data(PRODUCT_ID_ROLE):
|
if not index.data(PRODUCT_ID_ROLE):
|
||||||
return None
|
return None
|
||||||
if col == self.version_col:
|
role = self._display_role_mapping.get(col)
|
||||||
role = VERSION_NAME_ROLE
|
if role is None:
|
||||||
elif col == 1:
|
|
||||||
role = PRODUCT_TYPE_ROLE
|
|
||||||
elif col == 2:
|
|
||||||
role = FOLDER_LABEL_ROLE
|
|
||||||
elif col == 4:
|
|
||||||
role = VERSION_PUBLISH_TIME_ROLE
|
|
||||||
elif col == 5:
|
|
||||||
role = VERSION_AUTHOR_ROLE
|
|
||||||
elif col == 6:
|
|
||||||
role = VERSION_FRAME_RANGE_ROLE
|
|
||||||
elif col == 7:
|
|
||||||
role = VERSION_DURATION_ROLE
|
|
||||||
elif col == 8:
|
|
||||||
role = VERSION_HANDLES_ROLE
|
|
||||||
elif col == 9:
|
|
||||||
role = VERSION_STEP_ROLE
|
|
||||||
elif col == 10:
|
|
||||||
role = PRODUCT_IN_SCENE_ROLE
|
|
||||||
elif col == 11:
|
|
||||||
role = VERSION_AVAILABLE_ROLE
|
|
||||||
else:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
index = self.index(index.row(), 0, index.parent())
|
index = self.index(index.row(), 0, index.parent())
|
||||||
|
|
@ -312,6 +329,7 @@ class ProductsModel(QtGui.QStandardItemModel):
|
||||||
version_item.published_time, VERSION_PUBLISH_TIME_ROLE
|
version_item.published_time, VERSION_PUBLISH_TIME_ROLE
|
||||||
)
|
)
|
||||||
model_item.setData(version_item.author, VERSION_AUTHOR_ROLE)
|
model_item.setData(version_item.author, VERSION_AUTHOR_ROLE)
|
||||||
|
model_item.setData(version_item.status, VERSION_STATUS_NAME_ROLE)
|
||||||
model_item.setData(version_item.frame_range, VERSION_FRAME_RANGE_ROLE)
|
model_item.setData(version_item.frame_range, VERSION_FRAME_RANGE_ROLE)
|
||||||
model_item.setData(version_item.duration, VERSION_DURATION_ROLE)
|
model_item.setData(version_item.duration, VERSION_DURATION_ROLE)
|
||||||
model_item.setData(version_item.handles, VERSION_HANDLES_ROLE)
|
model_item.setData(version_item.handles, VERSION_HANDLES_ROLE)
|
||||||
|
|
@ -393,6 +411,11 @@ class ProductsModel(QtGui.QStandardItemModel):
|
||||||
|
|
||||||
self._last_project_name = project_name
|
self._last_project_name = project_name
|
||||||
self._last_folder_ids = folder_ids
|
self._last_folder_ids = folder_ids
|
||||||
|
status_items = self._controller.get_project_status_items(project_name)
|
||||||
|
self._last_project_statuses = {
|
||||||
|
status_item.name: status_item
|
||||||
|
for status_item in status_items
|
||||||
|
}
|
||||||
|
|
||||||
active_site_icon_def = self._controller.get_active_site_icon_def(
|
active_site_icon_def = self._controller.get_active_site_icon_def(
|
||||||
project_name
|
project_name
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ from .products_model import (
|
||||||
from .products_delegates import (
|
from .products_delegates import (
|
||||||
VersionDelegate,
|
VersionDelegate,
|
||||||
LoadedInSceneDelegate,
|
LoadedInSceneDelegate,
|
||||||
SiteSyncDelegate
|
StatusDelegate,
|
||||||
|
SiteSyncDelegate,
|
||||||
)
|
)
|
||||||
from .actions_utils import show_actions_menu
|
from .actions_utils import show_actions_menu
|
||||||
|
|
||||||
|
|
@ -89,6 +90,7 @@ class ProductsWidget(QtWidgets.QWidget):
|
||||||
90, # Product type
|
90, # Product type
|
||||||
130, # Folder label
|
130, # Folder label
|
||||||
60, # Version
|
60, # Version
|
||||||
|
100, # Status
|
||||||
125, # Time
|
125, # Time
|
||||||
75, # Author
|
75, # Author
|
||||||
75, # Frames
|
75, # Frames
|
||||||
|
|
@ -128,20 +130,19 @@ class ProductsWidget(QtWidgets.QWidget):
|
||||||
products_view.setColumnWidth(idx, width)
|
products_view.setColumnWidth(idx, width)
|
||||||
|
|
||||||
version_delegate = VersionDelegate()
|
version_delegate = VersionDelegate()
|
||||||
products_view.setItemDelegateForColumn(
|
|
||||||
products_model.version_col, version_delegate)
|
|
||||||
|
|
||||||
time_delegate = PrettyTimeDelegate()
|
time_delegate = PrettyTimeDelegate()
|
||||||
products_view.setItemDelegateForColumn(
|
status_delegate = StatusDelegate()
|
||||||
products_model.published_time_col, time_delegate)
|
|
||||||
|
|
||||||
in_scene_delegate = LoadedInSceneDelegate()
|
in_scene_delegate = LoadedInSceneDelegate()
|
||||||
products_view.setItemDelegateForColumn(
|
|
||||||
products_model.in_scene_col, in_scene_delegate)
|
|
||||||
|
|
||||||
sitesync_delegate = SiteSyncDelegate()
|
sitesync_delegate = SiteSyncDelegate()
|
||||||
products_view.setItemDelegateForColumn(
|
|
||||||
products_model.sitesync_avail_col, sitesync_delegate)
|
for col, delegate in (
|
||||||
|
(products_model.version_col, version_delegate),
|
||||||
|
(products_model.published_time_col, time_delegate),
|
||||||
|
(products_model.status_col, status_delegate),
|
||||||
|
(products_model.in_scene_col, in_scene_delegate),
|
||||||
|
(products_model.sitesync_avail_col, sitesync_delegate),
|
||||||
|
):
|
||||||
|
products_view.setItemDelegateForColumn(col, delegate)
|
||||||
|
|
||||||
main_layout = QtWidgets.QHBoxLayout(self)
|
main_layout = QtWidgets.QHBoxLayout(self)
|
||||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
@ -175,6 +176,7 @@ class ProductsWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
self._version_delegate = version_delegate
|
self._version_delegate = version_delegate
|
||||||
self._time_delegate = time_delegate
|
self._time_delegate = time_delegate
|
||||||
|
self._status_delegate = status_delegate
|
||||||
self._in_scene_delegate = in_scene_delegate
|
self._in_scene_delegate = in_scene_delegate
|
||||||
self._sitesync_delegate = sitesync_delegate
|
self._sitesync_delegate = sitesync_delegate
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Package declaring AYON core addon version."""
|
"""Package declaring AYON core addon version."""
|
||||||
__version__ = "0.3.2-dev.1"
|
__version__ = "0.3.3-dev.1"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name = "core"
|
name = "core"
|
||||||
title = "Core"
|
title = "Core"
|
||||||
version = "0.3.2-dev.1"
|
version = "0.3.3-dev.1"
|
||||||
|
|
||||||
client_dir = "ayon_core"
|
client_dir = "ayon_core"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
name = "blender"
|
name = "blender"
|
||||||
title = "Blender"
|
title = "Blender"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,10 @@ class PublishPluginsModel(BaseSettingsModel):
|
||||||
default_factory=ExtractPlayblastModel,
|
default_factory=ExtractPlayblastModel,
|
||||||
title="Extract Playblast"
|
title="Extract Playblast"
|
||||||
)
|
)
|
||||||
|
ExtractModelUSD: ValidatePluginModel = SettingsField(
|
||||||
|
default_factory=ValidatePluginModel,
|
||||||
|
title="Extract Model USD"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_BLENDER_PUBLISH_SETTINGS = {
|
DEFAULT_BLENDER_PUBLISH_SETTINGS = {
|
||||||
|
|
@ -348,5 +352,10 @@ DEFAULT_BLENDER_PUBLISH_SETTINGS = {
|
||||||
},
|
},
|
||||||
indent=4
|
indent=4
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
"ExtractModelUSD": {
|
||||||
|
"enabled": True,
|
||||||
|
"optional": True,
|
||||||
|
"active": True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
server_addon/clockify/client/ayon_clockify/__init__.py
Normal file
5
server_addon/clockify/client/ayon_clockify/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
from .addon import ClockifyAddon
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"ClockifyAddon",
|
||||||
|
)
|
||||||
|
|
@ -2,12 +2,12 @@ import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from ayon_core.modules import AYONAddon, ITrayModule, IPluginPaths
|
from ayon_core.addon import AYONAddon, ITrayAddon, IPluginPaths
|
||||||
|
|
||||||
from .constants import CLOCKIFY_FTRACK_USER_PATH, CLOCKIFY_FTRACK_SERVER_PATH
|
from .constants import CLOCKIFY_FTRACK_USER_PATH, CLOCKIFY_FTRACK_SERVER_PATH
|
||||||
|
|
||||||
|
|
||||||
class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
|
class ClockifyAddon(AYONAddon, ITrayAddon, IPluginPaths):
|
||||||
name = "clockify"
|
name = "clockify"
|
||||||
|
|
||||||
def initialize(self, studio_settings):
|
def initialize(self, studio_settings):
|
||||||
|
|
@ -31,7 +31,7 @@ class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
|
||||||
# TimersManager attributes
|
# TimersManager attributes
|
||||||
# - set `timers_manager_connector` only in `tray_init`
|
# - set `timers_manager_connector` only in `tray_init`
|
||||||
self.timers_manager_connector = None
|
self.timers_manager_connector = None
|
||||||
self._timers_manager_module = None
|
self._timer_manager_addon = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def clockify_api(self):
|
def clockify_api(self):
|
||||||
|
|
@ -87,7 +87,7 @@ class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
|
||||||
return {"actions": [actions_path]}
|
return {"actions": [actions_path]}
|
||||||
|
|
||||||
def get_ftrack_event_handler_paths(self):
|
def get_ftrack_event_handler_paths(self):
|
||||||
"""Function for Ftrack module to add ftrack event handler paths."""
|
"""Function for ftrack addon to add ftrack event handler paths."""
|
||||||
return {
|
return {
|
||||||
"user": [CLOCKIFY_FTRACK_USER_PATH],
|
"user": [CLOCKIFY_FTRACK_USER_PATH],
|
||||||
"server": [CLOCKIFY_FTRACK_SERVER_PATH],
|
"server": [CLOCKIFY_FTRACK_SERVER_PATH],
|
||||||
|
|
@ -206,19 +206,19 @@ class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
|
||||||
self.action_stop_timer.setVisible(self.bool_timer_run)
|
self.action_stop_timer.setVisible(self.bool_timer_run)
|
||||||
|
|
||||||
# --- TimersManager connection methods ---
|
# --- TimersManager connection methods ---
|
||||||
def register_timers_manager(self, timer_manager_module):
|
def register_timers_manager(self, timer_manager_addon):
|
||||||
"""Store TimersManager for future use."""
|
"""Store TimersManager for future use."""
|
||||||
self._timers_manager_module = timer_manager_module
|
self._timer_manager_addon = timer_manager_addon
|
||||||
|
|
||||||
def timer_started(self, data):
|
def timer_started(self, data):
|
||||||
"""Tell TimersManager that timer started."""
|
"""Tell TimersManager that timer started."""
|
||||||
if self._timers_manager_module is not None:
|
if self._timer_manager_addon is not None:
|
||||||
self._timers_manager_module.timer_started(self.id, data)
|
self._timer_manager_addon.timer_started(self.id, data)
|
||||||
|
|
||||||
def timer_stopped(self):
|
def timer_stopped(self):
|
||||||
"""Tell TimersManager that timer stopped."""
|
"""Tell TimersManager that timer stopped."""
|
||||||
if self._timers_manager_module is not None:
|
if self._timer_manager_addon is not None:
|
||||||
self._timers_manager_module.timer_stopped(self.id)
|
self._timer_manager_addon.timer_stopped(self.id)
|
||||||
|
|
||||||
def stop_timer(self):
|
def stop_timer(self):
|
||||||
"""Called from TimersManager to stop timer."""
|
"""Called from TimersManager to stop timer."""
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from ayon_core.lib.local_settings import AYONSecureRegistry
|
||||||
|
from ayon_core.lib import Logger
|
||||||
|
|
||||||
from .constants import (
|
from .constants import (
|
||||||
CLOCKIFY_ENDPOINT,
|
CLOCKIFY_ENDPOINT,
|
||||||
ADMIN_PERMISSION_NAMES,
|
ADMIN_PERMISSION_NAMES,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ayon_core.lib.local_settings import AYONSecureRegistry
|
|
||||||
from ayon_core.lib import Logger
|
|
||||||
|
|
||||||
|
|
||||||
class ClockifyAPI:
|
class ClockifyAPI:
|
||||||
log = Logger.get_logger(__name__)
|
log = Logger.get_logger(__name__)
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from openpype_modules.ftrack.lib import ServerAction
|
|
||||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
from ayon_clockify.clockify_api import ClockifyAPI
|
||||||
|
|
||||||
|
from ayon_ftrack.lib import ServerAction
|
||||||
|
|
||||||
|
|
||||||
class SyncClockifyServer(ServerAction):
|
class SyncClockifyServer(ServerAction):
|
||||||
|
|
@ -1,25 +1,20 @@
|
||||||
import json
|
import json
|
||||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
from ayon_clockify.clockify_api import ClockifyAPI
|
||||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
from ayon_ftrack.lib import BaseAction, statics_icon
|
||||||
|
|
||||||
|
|
||||||
class SyncClockifyLocal(BaseAction):
|
class SyncClockifyLocal(BaseAction):
|
||||||
'''Synchronise project names and task types.'''
|
"""Synchronise project names and task types."""
|
||||||
|
|
||||||
#: Action identifier.
|
identifier = "clockify.sync.local"
|
||||||
identifier = 'clockify.sync.local'
|
label = "Sync To Clockify"
|
||||||
#: Action label.
|
description = "Synchronise data to Clockify workspace"
|
||||||
label = 'Sync To Clockify (local)'
|
|
||||||
#: Action description.
|
|
||||||
description = 'Synchronise data to Clockify workspace'
|
|
||||||
#: roles that are allowed to register this action
|
|
||||||
role_list = ["Administrator", "project Manager"]
|
role_list = ["Administrator", "project Manager"]
|
||||||
#: icon
|
|
||||||
icon = statics_icon("app_icons", "clockify-white.png")
|
icon = statics_icon("app_icons", "clockify-white.png")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SyncClockifyLocal, self).__init__(*args, **kwargs)
|
super(SyncClockifyLocal, self).__init__(*args, **kwargs)
|
||||||
#: CLockifyApi
|
|
||||||
self.clockify_api = ClockifyAPI()
|
self.clockify_api = ClockifyAPI()
|
||||||
|
|
||||||
def discover(self, session, entities, event):
|
def discover(self, session, entities, event):
|
||||||
|
|
@ -56,7 +51,7 @@ class SyncClockifyLocal(BaseAction):
|
||||||
'user': user,
|
'user': user,
|
||||||
'status': 'running',
|
'status': 'running',
|
||||||
'data': json.dumps({
|
'data': json.dumps({
|
||||||
'description': 'Sync Ftrack to Clockify'
|
'description': 'Sync ftrack to Clockify'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import ayon_api
|
import ayon_api
|
||||||
|
|
||||||
|
from ayon_clockify.clockify_api import ClockifyAPI
|
||||||
|
|
||||||
from ayon_core.pipeline import LauncherAction
|
from ayon_core.pipeline import LauncherAction
|
||||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
|
||||||
|
|
||||||
|
|
||||||
class ClockifyStart(LauncherAction):
|
class ClockifyStart(LauncherAction):
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import ayon_api
|
import ayon_api
|
||||||
|
|
||||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
from ayon_clockify.clockify_api import ClockifyAPI
|
||||||
from ayon_core.pipeline import LauncherAction
|
from ayon_core.pipeline import LauncherAction
|
||||||
|
|
||||||
|
|
||||||
3
server_addon/clockify/client/ayon_clockify/version.py
Normal file
3
server_addon/clockify/client/ayon_clockify/version.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Package declaring AYON addon 'clockify' version."""
|
||||||
|
__version__ = "0.2.0"
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
name = "clockify"
|
name = "clockify"
|
||||||
title = "Clockify"
|
title = "Clockify"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
|
client_dir = "ayon_clockify"
|
||||||
|
|
||||||
|
ayon_required_addons = {
|
||||||
|
"core": ">0.3.2",
|
||||||
|
}
|
||||||
|
ayon_compatible_addons = {}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ plugin_for = ["ayon_server"]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
CLIENT_VERSION_CONTENT = '''# -*- coding: utf-8 -*-
|
CLIENT_VERSION_CONTENT = '''# -*- coding: utf-8 -*-
|
||||||
"""Package declaring AYON core addon version."""
|
"""Package declaring AYON addon '{}' version."""
|
||||||
__version__ = "{}"
|
__version__ = "{}"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
@ -183,6 +183,7 @@ def create_addon_zip(
|
||||||
|
|
||||||
|
|
||||||
def prepare_client_code(
|
def prepare_client_code(
|
||||||
|
addon_name: str,
|
||||||
addon_dir: Path,
|
addon_dir: Path,
|
||||||
addon_output_dir: Path,
|
addon_output_dir: Path,
|
||||||
addon_version: str
|
addon_version: str
|
||||||
|
|
@ -211,7 +212,9 @@ def prepare_client_code(
|
||||||
version_path = subpath / "version.py"
|
version_path = subpath / "version.py"
|
||||||
if version_path.exists():
|
if version_path.exists():
|
||||||
with open(version_path, "w") as stream:
|
with open(version_path, "w") as stream:
|
||||||
stream.write(CLIENT_VERSION_CONTENT.format(addon_version))
|
stream.write(
|
||||||
|
CLIENT_VERSION_CONTENT.format(addon_name, addon_version)
|
||||||
|
)
|
||||||
|
|
||||||
zip_filepath = private_dir / "client.zip"
|
zip_filepath = private_dir / "client.zip"
|
||||||
with ZipFileLongPaths(zip_filepath, "w", zipfile.ZIP_DEFLATED) as zipf:
|
with ZipFileLongPaths(zip_filepath, "w", zipfile.ZIP_DEFLATED) as zipf:
|
||||||
|
|
@ -262,7 +265,9 @@ def create_addon_package(
|
||||||
server_dir, addon_output_dir / "server", dirs_exist_ok=True
|
server_dir, addon_output_dir / "server", dirs_exist_ok=True
|
||||||
)
|
)
|
||||||
|
|
||||||
prepare_client_code(addon_dir, addon_output_dir, addon_version)
|
prepare_client_code(
|
||||||
|
package.name, addon_dir, addon_output_dir, addon_version
|
||||||
|
)
|
||||||
|
|
||||||
if create_zip:
|
if create_zip:
|
||||||
create_addon_zip(
|
create_addon_zip(
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@ class CreatePluginsModel(BaseSettingsModel):
|
||||||
CreateMantraROP: CreatorModel = SettingsField(
|
CreateMantraROP: CreatorModel = SettingsField(
|
||||||
default_factory=CreatorModel,
|
default_factory=CreatorModel,
|
||||||
title="Create Mantra ROP")
|
title="Create Mantra ROP")
|
||||||
|
CreateModel: CreatorModel = SettingsField(
|
||||||
|
default_factory=CreatorModel,
|
||||||
|
title="Create Model")
|
||||||
CreatePointCache: CreatorModel = SettingsField(
|
CreatePointCache: CreatorModel = SettingsField(
|
||||||
default_factory=CreatorModel,
|
default_factory=CreatorModel,
|
||||||
title="Create PointCache (Abc)")
|
title="Create PointCache (Abc)")
|
||||||
|
|
@ -124,6 +127,10 @@ DEFAULT_HOUDINI_CREATE_SETTINGS = {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"default_variants": ["Main"]
|
"default_variants": ["Main"]
|
||||||
},
|
},
|
||||||
|
"CreateModel": {
|
||||||
|
"enabled": True,
|
||||||
|
"default_variants": ["Main"]
|
||||||
|
},
|
||||||
"CreatePointCache": {
|
"CreatePointCache": {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"default_variants": ["Main"]
|
"default_variants": ["Main"]
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ def imprint(node, data, tab=None):
|
||||||
Examples:
|
Examples:
|
||||||
```
|
```
|
||||||
import nuke
|
import nuke
|
||||||
from ayon_core.hosts.nuke.api import lib
|
from ayon_nuke.api import lib
|
||||||
|
|
||||||
node = nuke.createNode("NoOp")
|
node = nuke.createNode("NoOp")
|
||||||
data = {
|
data = {
|
||||||
|
|
@ -419,7 +419,7 @@ def add_publish_knob(node):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
@deprecated("ayon_core.hosts.nuke.api.lib.set_node_data")
|
@deprecated("ayon_nuke.api.lib.set_node_data")
|
||||||
def set_avalon_knob_data(node, data=None, prefix="avalon:"):
|
def set_avalon_knob_data(node, data=None, prefix="avalon:"):
|
||||||
"""[DEPRECATED] Sets data into nodes's avalon knob
|
"""[DEPRECATED] Sets data into nodes's avalon knob
|
||||||
|
|
||||||
|
|
@ -485,7 +485,7 @@ def set_avalon_knob_data(node, data=None, prefix="avalon:"):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
@deprecated("ayon_core.hosts.nuke.api.lib.get_node_data")
|
@deprecated("ayon_nuke.api.lib.get_node_data")
|
||||||
def get_avalon_knob_data(node, prefix="avalon:", create=True):
|
def get_avalon_knob_data(node, prefix="avalon:", create=True):
|
||||||
"""[DEPRECATED] Gets a data from nodes's avalon knob
|
"""[DEPRECATED] Gets a data from nodes's avalon knob
|
||||||
|
|
||||||
|
|
@ -1024,6 +1024,18 @@ def script_name():
|
||||||
return nuke.root().knob("name").value()
|
return nuke.root().knob("name").value()
|
||||||
|
|
||||||
|
|
||||||
|
def add_button_render_on_farm(node):
|
||||||
|
name = "renderOnFarm"
|
||||||
|
label = "Render On Farm"
|
||||||
|
value = (
|
||||||
|
"from ayon_nuke.api.utils import submit_render_on_farm;"
|
||||||
|
"submit_render_on_farm(nuke.thisNode())"
|
||||||
|
)
|
||||||
|
knob = nuke.PyScript_Knob(name, label, value)
|
||||||
|
knob.clearFlag(nuke.STARTLINE)
|
||||||
|
node.addKnob(knob)
|
||||||
|
|
||||||
|
|
||||||
def add_button_write_to_read(node):
|
def add_button_write_to_read(node):
|
||||||
name = "createReadNode"
|
name = "createReadNode"
|
||||||
label = "Read From Rendered"
|
label = "Read From Rendered"
|
||||||
|
|
@ -1146,6 +1158,17 @@ def create_write_node(
|
||||||
Return:
|
Return:
|
||||||
node (obj): group node with avalon data as Knobs
|
node (obj): group node with avalon data as Knobs
|
||||||
'''
|
'''
|
||||||
|
# Ensure name does not contain any invalid characters.
|
||||||
|
special_chars = re.escape("!@#$%^&*()=[]{}|\\;',.<>/?~+-")
|
||||||
|
special_chars_regex = re.compile(f"[{special_chars}]")
|
||||||
|
found_special_characters = list(special_chars_regex.findall(name))
|
||||||
|
|
||||||
|
msg = (
|
||||||
|
f"Special characters found in name \"{name}\": "
|
||||||
|
f"{' '.join(found_special_characters)}"
|
||||||
|
)
|
||||||
|
assert not found_special_characters, msg
|
||||||
|
|
||||||
prenodes = prenodes or []
|
prenodes = prenodes or []
|
||||||
|
|
||||||
# filtering variables
|
# filtering variables
|
||||||
|
|
@ -1270,6 +1293,10 @@ def create_write_node(
|
||||||
link.setFlag(0x1000)
|
link.setFlag(0x1000)
|
||||||
GN.addKnob(link)
|
GN.addKnob(link)
|
||||||
|
|
||||||
|
# Adding render farm submission button.
|
||||||
|
if data.get("render_on_farm", False):
|
||||||
|
add_button_render_on_farm(GN)
|
||||||
|
|
||||||
# adding write to read button
|
# adding write to read button
|
||||||
add_button_write_to_read(GN)
|
add_button_write_to_read(GN)
|
||||||
|
|
||||||
|
|
@ -2442,7 +2469,7 @@ def _launch_workfile_app():
|
||||||
host_tools.show_workfiles(parent=None, on_top=True)
|
host_tools.show_workfiles(parent=None, on_top=True)
|
||||||
|
|
||||||
|
|
||||||
@deprecated("ayon_core.hosts.nuke.api.lib.start_workfile_template_builder")
|
@deprecated("ayon_nuke.api.lib.start_workfile_template_builder")
|
||||||
def process_workfile_builder():
|
def process_workfile_builder():
|
||||||
""" [DEPRECATED] Process workfile builder on nuke start
|
""" [DEPRECATED] Process workfile builder on nuke start
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ from ayon_core.pipeline import (
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline.workfile import BuildWorkfile
|
from ayon_core.pipeline.workfile import BuildWorkfile
|
||||||
from ayon_core.tools.utils import host_tools
|
from ayon_core.tools.utils import host_tools
|
||||||
from ayon_core.hosts.nuke import NUKE_ROOT_DIR
|
from ayon_nuke import NUKE_ROOT_DIR
|
||||||
from ayon_core.tools.workfile_template_build import open_template_ui
|
from ayon_core.tools.workfile_template_build import open_template_ui
|
||||||
|
|
||||||
from .lib import (
|
from .lib import (
|
||||||
|
|
@ -188,10 +188,10 @@ def reload_config():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for module in (
|
for module in (
|
||||||
"ayon_core.hosts.nuke.api.actions",
|
"ayon_nuke.api.actions",
|
||||||
"ayon_core.hosts.nuke.api.menu",
|
"ayon_nuke.api.menu",
|
||||||
"ayon_core.hosts.nuke.api.plugin",
|
"ayon_nuke.api.plugin",
|
||||||
"ayon_core.hosts.nuke.api.lib",
|
"ayon_nuke.api.lib",
|
||||||
):
|
):
|
||||||
log.info("Reloading module: {}...".format(module))
|
log.info("Reloading module: {}...".format(module))
|
||||||
|
|
||||||
|
|
@ -572,8 +572,11 @@ class ExporterReview(object):
|
||||||
self.fhead = self.fhead.replace("#", "")[:-1]
|
self.fhead = self.fhead.replace("#", "")[:-1]
|
||||||
|
|
||||||
def get_representation_data(
|
def get_representation_data(
|
||||||
self, tags=None, range=False,
|
self,
|
||||||
custom_tags=None, colorspace=None
|
tags=None,
|
||||||
|
range=False,
|
||||||
|
custom_tags=None,
|
||||||
|
colorspace=None,
|
||||||
):
|
):
|
||||||
""" Add representation data to self.data
|
""" Add representation data to self.data
|
||||||
|
|
||||||
|
|
@ -584,6 +587,8 @@ class ExporterReview(object):
|
||||||
Defaults to False.
|
Defaults to False.
|
||||||
custom_tags (list[str], optional): user inputted custom tags.
|
custom_tags (list[str], optional): user inputted custom tags.
|
||||||
Defaults to None.
|
Defaults to None.
|
||||||
|
colorspace (str, optional): colorspace name.
|
||||||
|
Defaults to None.
|
||||||
"""
|
"""
|
||||||
add_tags = tags or []
|
add_tags = tags or []
|
||||||
repre = {
|
repre = {
|
||||||
|
|
@ -591,7 +596,13 @@ class ExporterReview(object):
|
||||||
"ext": self.ext,
|
"ext": self.ext,
|
||||||
"files": self.file,
|
"files": self.file,
|
||||||
"stagingDir": self.staging_dir,
|
"stagingDir": self.staging_dir,
|
||||||
"tags": [self.name.replace("_", "-")] + add_tags
|
"tags": [self.name.replace("_", "-")] + add_tags,
|
||||||
|
"data": {
|
||||||
|
# making sure that once intermediate file is published
|
||||||
|
# as representation, we will be able to then identify it
|
||||||
|
# from representation.data.isIntermediate
|
||||||
|
"isIntermediate": True
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if custom_tags:
|
if custom_tags:
|
||||||
|
|
@ -1000,7 +1011,7 @@ class ExporterReviewMov(ExporterReview):
|
||||||
tags=tags + add_tags,
|
tags=tags + add_tags,
|
||||||
custom_tags=add_custom_tags,
|
custom_tags=add_custom_tags,
|
||||||
range=True,
|
range=True,
|
||||||
colorspace=colorspace
|
colorspace=colorspace,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.log.debug("Representation... `{}`".format(self.data))
|
self.log.debug("Representation... `{}`".format(self.data))
|
||||||
|
|
@ -1039,7 +1050,7 @@ def convert_to_valid_instaces():
|
||||||
}
|
}
|
||||||
return mapping[product_type]
|
return mapping[product_type]
|
||||||
|
|
||||||
from ayon_core.hosts.nuke.api import workio
|
from ayon_nuke.api import workio
|
||||||
|
|
||||||
task_name = get_current_task_name()
|
task_name = get_current_task_name()
|
||||||
|
|
||||||
|
|
@ -3,9 +3,15 @@ import re
|
||||||
|
|
||||||
import nuke
|
import nuke
|
||||||
|
|
||||||
from ayon_core import resources
|
import pyblish.util
|
||||||
|
import pyblish.api
|
||||||
from qtpy import QtWidgets
|
from qtpy import QtWidgets
|
||||||
|
|
||||||
|
from ayon_core import resources
|
||||||
|
from ayon_core.pipeline import registered_host
|
||||||
|
from ayon_core.tools.utils import show_message_dialog
|
||||||
|
from ayon_core.pipeline.create import CreateContext
|
||||||
|
|
||||||
|
|
||||||
def set_context_favorites(favorites=None):
|
def set_context_favorites(favorites=None):
|
||||||
""" Adding favorite folders to nuke's browser
|
""" Adding favorite folders to nuke's browser
|
||||||
|
|
@ -142,3 +148,77 @@ def is_headless():
|
||||||
bool: headless
|
bool: headless
|
||||||
"""
|
"""
|
||||||
return QtWidgets.QApplication.instance() is None
|
return QtWidgets.QApplication.instance() is None
|
||||||
|
|
||||||
|
|
||||||
|
def submit_render_on_farm(node):
|
||||||
|
# Ensure code is executed in root context.
|
||||||
|
if nuke.root() == nuke.thisNode():
|
||||||
|
_submit_render_on_farm(node)
|
||||||
|
else:
|
||||||
|
# If not in root context, move to the root context and then execute the
|
||||||
|
# code.
|
||||||
|
with nuke.root():
|
||||||
|
_submit_render_on_farm(node)
|
||||||
|
|
||||||
|
|
||||||
|
def _submit_render_on_farm(node):
|
||||||
|
"""Render on farm submission
|
||||||
|
|
||||||
|
This function prepares the context for farm submission, validates it,
|
||||||
|
extracts relevant data, copies the current workfile to a timestamped copy,
|
||||||
|
and submits the job to the farm.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node (Node): The node for which the farm submission is being made.
|
||||||
|
"""
|
||||||
|
|
||||||
|
host = registered_host()
|
||||||
|
create_context = CreateContext(host)
|
||||||
|
|
||||||
|
# Ensure CreateInstance is enabled.
|
||||||
|
for instance in create_context.instances:
|
||||||
|
if node.name() != instance.transient_data["node"].name():
|
||||||
|
continue
|
||||||
|
|
||||||
|
instance.data["active"] = True
|
||||||
|
|
||||||
|
context = pyblish.api.Context()
|
||||||
|
context.data["create_context"] = create_context
|
||||||
|
# Used in pyblish plugin to determine which instance to publish.
|
||||||
|
context.data["node_name"] = node.name()
|
||||||
|
# Used in pyblish plugins to determine whether to run or not.
|
||||||
|
context.data["render_on_farm"] = True
|
||||||
|
|
||||||
|
# Since we need to bypass version validation and incrementing, we need to
|
||||||
|
# remove the plugins from the list that are responsible for these tasks.
|
||||||
|
plugins = pyblish.api.discover()
|
||||||
|
blacklist = ["IncrementScriptVersion", "ValidateVersion"]
|
||||||
|
plugins = [
|
||||||
|
plugin
|
||||||
|
for plugin in plugins
|
||||||
|
if plugin.__name__ not in blacklist
|
||||||
|
]
|
||||||
|
|
||||||
|
context = pyblish.util.publish(context, plugins=plugins)
|
||||||
|
|
||||||
|
error_message = ""
|
||||||
|
success = True
|
||||||
|
for result in context.data["results"]:
|
||||||
|
if result["success"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
success = False
|
||||||
|
|
||||||
|
err = result["error"]
|
||||||
|
error_message += "\n"
|
||||||
|
error_message += err.formatted_traceback
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
show_message_dialog(
|
||||||
|
"Publish Errors", error_message, level="critical"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
show_message_dialog(
|
||||||
|
"Submission Successful", "Submission to the farm was successful."
|
||||||
|
)
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||||
from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin
|
from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
INSTANCE_DATA_KNOB,
|
INSTANCE_DATA_KNOB,
|
||||||
get_node_data,
|
get_node_data,
|
||||||
get_avalon_knob_data,
|
get_avalon_knob_data,
|
||||||
NODE_TAB_NAME,
|
NODE_TAB_NAME,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.plugin import convert_to_valid_instaces
|
from ayon_nuke.api.plugin import convert_to_valid_instaces
|
||||||
|
|
||||||
import nuke
|
import nuke
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from nukescripts import autoBackdrop
|
from nukescripts import autoBackdrop
|
||||||
|
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
NukeCreator,
|
NukeCreator,
|
||||||
maintained_selection,
|
maintained_selection,
|
||||||
select_nodes
|
select_nodes
|
||||||
|
|
@ -10,6 +10,8 @@ from ayon_core.hosts.nuke.api import (
|
||||||
class CreateBackdrop(NukeCreator):
|
class CreateBackdrop(NukeCreator):
|
||||||
"""Add Publishable Backdrop"""
|
"""Add Publishable Backdrop"""
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_backdrop"
|
identifier = "create_backdrop"
|
||||||
label = "Nukenodes (backdrop)"
|
label = "Nukenodes (backdrop)"
|
||||||
product_type = "nukenodes"
|
product_type = "nukenodes"
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import nuke
|
import nuke
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
NukeCreator,
|
NukeCreator,
|
||||||
NukeCreatorError,
|
NukeCreatorError,
|
||||||
maintained_selection
|
maintained_selection
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
create_camera_node_by_version
|
create_camera_node_by_version
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -12,6 +12,8 @@ from ayon_core.hosts.nuke.api.lib import (
|
||||||
class CreateCamera(NukeCreator):
|
class CreateCamera(NukeCreator):
|
||||||
"""Add Publishable Camera"""
|
"""Add Publishable Camera"""
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_camera"
|
identifier = "create_camera"
|
||||||
label = "Camera (3d)"
|
label = "Camera (3d)"
|
||||||
product_type = "camera"
|
product_type = "camera"
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import nuke
|
import nuke
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
NukeCreator,
|
NukeCreator,
|
||||||
NukeCreatorError,
|
NukeCreatorError,
|
||||||
maintained_selection
|
maintained_selection
|
||||||
|
|
@ -9,6 +9,8 @@ from ayon_core.hosts.nuke.api import (
|
||||||
class CreateGizmo(NukeCreator):
|
class CreateGizmo(NukeCreator):
|
||||||
"""Add Publishable Group as gizmo"""
|
"""Add Publishable Group as gizmo"""
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_gizmo"
|
identifier = "create_gizmo"
|
||||||
label = "Gizmo (group)"
|
label = "Gizmo (group)"
|
||||||
product_type = "gizmo"
|
product_type = "gizmo"
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import nuke
|
import nuke
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
NukeCreator,
|
NukeCreator,
|
||||||
NukeCreatorError,
|
NukeCreatorError,
|
||||||
maintained_selection
|
maintained_selection
|
||||||
|
|
@ -9,6 +9,8 @@ from ayon_core.hosts.nuke.api import (
|
||||||
class CreateModel(NukeCreator):
|
class CreateModel(NukeCreator):
|
||||||
"""Add Publishable Camera"""
|
"""Add Publishable Camera"""
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_model"
|
identifier = "create_model"
|
||||||
label = "Model (3d)"
|
label = "Model (3d)"
|
||||||
product_type = "model"
|
product_type = "model"
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import nuke
|
import nuke
|
||||||
import six
|
import six
|
||||||
import sys
|
import sys
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
INSTANCE_DATA_KNOB,
|
INSTANCE_DATA_KNOB,
|
||||||
NukeCreator,
|
NukeCreator,
|
||||||
NukeCreatorError,
|
NukeCreatorError,
|
||||||
|
|
@ -15,6 +15,8 @@ from ayon_core.pipeline import (
|
||||||
class CreateSource(NukeCreator):
|
class CreateSource(NukeCreator):
|
||||||
"""Add Publishable Read with source"""
|
"""Add Publishable Read with source"""
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_source"
|
identifier = "create_source"
|
||||||
label = "Source (read)"
|
label = "Source (read)"
|
||||||
product_type = "source"
|
product_type = "source"
|
||||||
|
|
@ -11,11 +11,14 @@ from ayon_core.lib import (
|
||||||
UISeparatorDef,
|
UISeparatorDef,
|
||||||
EnumDef
|
EnumDef
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke import api as napi
|
from ayon_nuke import api as napi
|
||||||
from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs
|
from ayon_nuke.api.plugin import exposed_write_knobs
|
||||||
|
|
||||||
|
|
||||||
class CreateWriteImage(napi.NukeWriteCreator):
|
class CreateWriteImage(napi.NukeWriteCreator):
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_write_image"
|
identifier = "create_write_image"
|
||||||
label = "Image (write)"
|
label = "Image (write)"
|
||||||
product_type = "image"
|
product_type = "image"
|
||||||
|
|
@ -65,12 +68,16 @@ class CreateWriteImage(napi.NukeWriteCreator):
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_instance_node(self, product_name, instance_data):
|
def create_instance_node(self, product_name, instance_data):
|
||||||
|
settings = self.project_settings["nuke"]["create"]["CreateWriteImage"]
|
||||||
|
|
||||||
# add fpath_template
|
# add fpath_template
|
||||||
write_data = {
|
write_data = {
|
||||||
"creator": self.__class__.__name__,
|
"creator": self.__class__.__name__,
|
||||||
"productName": product_name,
|
"productName": product_name,
|
||||||
"fpath_template": self.temp_rendering_path_template
|
"fpath_template": self.temp_rendering_path_template,
|
||||||
|
"render_on_farm": (
|
||||||
|
"render_on_farm" in settings["instance_attributes"]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
write_data.update(instance_data)
|
write_data.update(instance_data)
|
||||||
|
|
||||||
|
|
@ -8,11 +8,14 @@ from ayon_core.pipeline import (
|
||||||
from ayon_core.lib import (
|
from ayon_core.lib import (
|
||||||
BoolDef
|
BoolDef
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke import api as napi
|
from ayon_nuke import api as napi
|
||||||
from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs
|
from ayon_nuke.api.plugin import exposed_write_knobs
|
||||||
|
|
||||||
|
|
||||||
class CreateWritePrerender(napi.NukeWriteCreator):
|
class CreateWritePrerender(napi.NukeWriteCreator):
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_write_prerender"
|
identifier = "create_write_prerender"
|
||||||
label = "Prerender (write)"
|
label = "Prerender (write)"
|
||||||
product_type = "prerender"
|
product_type = "prerender"
|
||||||
|
|
@ -46,11 +49,17 @@ class CreateWritePrerender(napi.NukeWriteCreator):
|
||||||
return attr_defs
|
return attr_defs
|
||||||
|
|
||||||
def create_instance_node(self, product_name, instance_data):
|
def create_instance_node(self, product_name, instance_data):
|
||||||
|
settings = self.project_settings["nuke"]["create"]
|
||||||
|
settings = settings["CreateWritePrerender"]
|
||||||
|
|
||||||
# add fpath_template
|
# add fpath_template
|
||||||
write_data = {
|
write_data = {
|
||||||
"creator": self.__class__.__name__,
|
"creator": self.__class__.__name__,
|
||||||
"productName": product_name,
|
"productName": product_name,
|
||||||
"fpath_template": self.temp_rendering_path_template
|
"fpath_template": self.temp_rendering_path_template,
|
||||||
|
"render_on_farm": (
|
||||||
|
"render_on_farm" in settings["instance_attributes"]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
write_data.update(instance_data)
|
write_data.update(instance_data)
|
||||||
|
|
@ -8,11 +8,14 @@ from ayon_core.pipeline import (
|
||||||
from ayon_core.lib import (
|
from ayon_core.lib import (
|
||||||
BoolDef
|
BoolDef
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke import api as napi
|
from ayon_nuke import api as napi
|
||||||
from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs
|
from ayon_nuke.api.plugin import exposed_write_knobs
|
||||||
|
|
||||||
|
|
||||||
class CreateWriteRender(napi.NukeWriteCreator):
|
class CreateWriteRender(napi.NukeWriteCreator):
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "create_write_render"
|
identifier = "create_write_render"
|
||||||
label = "Render (write)"
|
label = "Render (write)"
|
||||||
product_type = "render"
|
product_type = "render"
|
||||||
|
|
@ -40,11 +43,16 @@ class CreateWriteRender(napi.NukeWriteCreator):
|
||||||
return attr_defs
|
return attr_defs
|
||||||
|
|
||||||
def create_instance_node(self, product_name, instance_data):
|
def create_instance_node(self, product_name, instance_data):
|
||||||
|
settings = self.project_settings["nuke"]["create"]["CreateWriteRender"]
|
||||||
|
|
||||||
# add fpath_template
|
# add fpath_template
|
||||||
write_data = {
|
write_data = {
|
||||||
"creator": self.__class__.__name__,
|
"creator": self.__class__.__name__,
|
||||||
"productName": product_name,
|
"productName": product_name,
|
||||||
"fpath_template": self.temp_rendering_path_template
|
"fpath_template": self.temp_rendering_path_template,
|
||||||
|
"render_on_farm": (
|
||||||
|
"render_on_farm" in settings["instance_attributes"]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
write_data.update(instance_data)
|
write_data.update(instance_data)
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import ayon_api
|
import ayon_api
|
||||||
|
|
||||||
import ayon_core.hosts.nuke.api as api
|
import ayon_nuke.api as api
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
AutoCreator,
|
AutoCreator,
|
||||||
CreatedInstance,
|
CreatedInstance,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
INSTANCE_DATA_KNOB,
|
INSTANCE_DATA_KNOB,
|
||||||
set_node_data
|
set_node_data
|
||||||
)
|
)
|
||||||
|
|
@ -13,6 +13,9 @@ import nuke
|
||||||
|
|
||||||
|
|
||||||
class WorkfileCreator(AutoCreator):
|
class WorkfileCreator(AutoCreator):
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
identifier = "workfile"
|
identifier = "workfile"
|
||||||
product_type = "workfile"
|
product_type = "workfile"
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from ayon_core.lib import Logger
|
from ayon_core.lib import Logger
|
||||||
from ayon_core.pipeline import InventoryAction
|
from ayon_core.pipeline import InventoryAction
|
||||||
from ayon_core.hosts.nuke.api.lib import set_avalon_knob_data
|
from ayon_nuke.api.lib import set_avalon_knob_data
|
||||||
|
|
||||||
|
|
||||||
class RepairOldLoaders(InventoryAction):
|
class RepairOldLoaders(InventoryAction):
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from ayon_core.pipeline import InventoryAction
|
from ayon_core.pipeline import InventoryAction
|
||||||
from ayon_core.hosts.nuke.api.command import viewer_update_and_undo_stop
|
from ayon_nuke.api.command import viewer_update_and_undo_stop
|
||||||
|
|
||||||
|
|
||||||
class SelectContainers(InventoryAction):
|
class SelectContainers(InventoryAction):
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
from ayon_core.lib import Logger
|
from ayon_core.lib import Logger
|
||||||
from ayon_core.pipeline import load
|
from ayon_core.pipeline import load
|
||||||
from ayon_core.hosts.nuke.api import lib
|
from ayon_nuke.api import lib
|
||||||
|
|
||||||
log = Logger.get_logger(__name__)
|
log = Logger.get_logger(__name__)
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
find_free_space_to_paste_nodes,
|
find_free_space_to_paste_nodes,
|
||||||
maintained_selection,
|
maintained_selection,
|
||||||
reset_selection,
|
reset_selection,
|
||||||
|
|
@ -14,8 +14,8 @@ from ayon_core.hosts.nuke.api.lib import (
|
||||||
get_avalon_knob_data,
|
get_avalon_knob_data,
|
||||||
set_avalon_knob_data
|
set_avalon_knob_data
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.command import viewer_update_and_undo_stop
|
from ayon_nuke.api.command import viewer_update_and_undo_stop
|
||||||
from ayon_core.hosts.nuke.api import containerise, update_container
|
from ayon_nuke.api import containerise, update_container
|
||||||
|
|
||||||
|
|
||||||
class LoadBackdropNodes(load.LoaderPlugin):
|
class LoadBackdropNodes(load.LoaderPlugin):
|
||||||
|
|
@ -25,6 +25,8 @@ class LoadBackdropNodes(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"nk"}
|
extensions = {"nk"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Import Nuke Nodes"
|
label = "Import Nuke Nodes"
|
||||||
order = 0
|
order = 0
|
||||||
icon = "eye"
|
icon = "eye"
|
||||||
|
|
@ -5,12 +5,12 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
maintained_selection
|
maintained_selection
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -24,6 +24,8 @@ class AlembicCameraLoader(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"abc"}
|
extensions = {"abc"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Alembic Camera"
|
label = "Load Alembic Camera"
|
||||||
icon = "camera"
|
icon = "camera"
|
||||||
color = "orange"
|
color = "orange"
|
||||||
|
|
@ -12,11 +12,11 @@ from ayon_core.pipeline.colorspace import (
|
||||||
get_imageio_file_rules_colorspace_from_filepath,
|
get_imageio_file_rules_colorspace_from_filepath,
|
||||||
get_current_context_imageio_config_preset,
|
get_current_context_imageio_config_preset,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
get_imageio_input_colorspace,
|
get_imageio_input_colorspace,
|
||||||
maintained_selection
|
maintained_selection
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop,
|
viewer_update_and_undo_stop,
|
||||||
|
|
@ -26,7 +26,7 @@ from ayon_core.lib.transcoding import (
|
||||||
VIDEO_EXTENSIONS,
|
VIDEO_EXTENSIONS,
|
||||||
IMAGE_EXTENSIONS
|
IMAGE_EXTENSIONS
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import plugin
|
from ayon_nuke.api import plugin
|
||||||
|
|
||||||
|
|
||||||
class LoadClip(plugin.NukeLoader):
|
class LoadClip(plugin.NukeLoader):
|
||||||
|
|
@ -48,6 +48,8 @@ class LoadClip(plugin.NukeLoader):
|
||||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Clip"
|
label = "Load Clip"
|
||||||
order = -20
|
order = -20
|
||||||
icon = "file-video-o"
|
icon = "file-video-o"
|
||||||
|
|
@ -61,7 +63,8 @@ class LoadClip(plugin.NukeLoader):
|
||||||
# option gui
|
# option gui
|
||||||
options_defaults = {
|
options_defaults = {
|
||||||
"start_at_workfile": True,
|
"start_at_workfile": True,
|
||||||
"add_retime": True
|
"add_retime": True,
|
||||||
|
"deep_exr": False
|
||||||
}
|
}
|
||||||
|
|
||||||
node_name_template = "{class_name}_{ext}"
|
node_name_template = "{class_name}_{ext}"
|
||||||
|
|
@ -78,6 +81,11 @@ class LoadClip(plugin.NukeLoader):
|
||||||
"add_retime",
|
"add_retime",
|
||||||
help="Load with retime",
|
help="Load with retime",
|
||||||
default=cls.options_defaults["add_retime"]
|
default=cls.options_defaults["add_retime"]
|
||||||
|
),
|
||||||
|
qargparse.Boolean(
|
||||||
|
"deep_exr",
|
||||||
|
help="Read with deep exr",
|
||||||
|
default=cls.options_defaults["deep_exr"]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -113,6 +121,9 @@ class LoadClip(plugin.NukeLoader):
|
||||||
add_retime = options.get(
|
add_retime = options.get(
|
||||||
"add_retime", self.options_defaults["add_retime"])
|
"add_retime", self.options_defaults["add_retime"])
|
||||||
|
|
||||||
|
deep_exr = options.get(
|
||||||
|
"deep_exr", self.options_defaults["deep_exr"])
|
||||||
|
|
||||||
repre_id = repre_entity["id"]
|
repre_id = repre_entity["id"]
|
||||||
|
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
|
|
@ -153,13 +164,21 @@ class LoadClip(plugin.NukeLoader):
|
||||||
return
|
return
|
||||||
|
|
||||||
read_name = self._get_node_name(context)
|
read_name = self._get_node_name(context)
|
||||||
|
read_node = None
|
||||||
# Create the Loader with the filename path set
|
if deep_exr:
|
||||||
read_node = nuke.createNode(
|
# Create the Loader with the filename path set
|
||||||
"Read",
|
read_node = nuke.createNode(
|
||||||
"name {}".format(read_name),
|
"DeepRead",
|
||||||
inpanel=False
|
"name {}".format(read_name),
|
||||||
)
|
inpanel=False
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Create the Loader with the filename path set
|
||||||
|
read_node = nuke.createNode(
|
||||||
|
"Read",
|
||||||
|
"name {}".format(read_name),
|
||||||
|
inpanel=False
|
||||||
|
)
|
||||||
|
|
||||||
# get colorspace
|
# get colorspace
|
||||||
colorspace = (
|
colorspace = (
|
||||||
|
|
@ -171,14 +190,14 @@ class LoadClip(plugin.NukeLoader):
|
||||||
# we will switch off undo-ing
|
# we will switch off undo-ing
|
||||||
with viewer_update_and_undo_stop():
|
with viewer_update_and_undo_stop():
|
||||||
read_node["file"].setValue(filepath)
|
read_node["file"].setValue(filepath)
|
||||||
|
if read_node.Class() == "Read":
|
||||||
self.set_colorspace_to_node(
|
self.set_colorspace_to_node(
|
||||||
read_node,
|
read_node,
|
||||||
filepath,
|
filepath,
|
||||||
project_name,
|
project_name,
|
||||||
version_entity,
|
version_entity,
|
||||||
repre_entity
|
repre_entity
|
||||||
)
|
)
|
||||||
|
|
||||||
self._set_range_to_node(
|
self._set_range_to_node(
|
||||||
read_node, first, last, start_at_workfile, slate_frames
|
read_node, first, last, start_at_workfile, slate_frames
|
||||||
|
|
@ -328,13 +347,14 @@ class LoadClip(plugin.NukeLoader):
|
||||||
# to avoid multiple undo steps for rest of process
|
# to avoid multiple undo steps for rest of process
|
||||||
# we will switch off undo-ing
|
# we will switch off undo-ing
|
||||||
with viewer_update_and_undo_stop():
|
with viewer_update_and_undo_stop():
|
||||||
self.set_colorspace_to_node(
|
if read_node.Class() == "Read":
|
||||||
read_node,
|
self.set_colorspace_to_node(
|
||||||
filepath,
|
read_node,
|
||||||
project_name,
|
filepath,
|
||||||
version_entity,
|
project_name,
|
||||||
repre_entity
|
version_entity,
|
||||||
)
|
repre_entity
|
||||||
|
)
|
||||||
|
|
||||||
self._set_range_to_node(read_node, first, last, start_at_workfile)
|
self._set_range_to_node(read_node, first, last, start_at_workfile)
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
|
|
@ -22,13 +22,14 @@ class LoadEffects(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"json"}
|
extensions = {"json"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Effects - nodes"
|
label = "Load Effects - nodes"
|
||||||
order = 0
|
order = 0
|
||||||
icon = "cc"
|
icon = "cc"
|
||||||
color = "white"
|
color = "white"
|
||||||
ignore_attr = ["useLifetime"]
|
ignore_attr = ["useLifetime"]
|
||||||
|
|
||||||
|
|
||||||
def load(self, context, name, namespace, data):
|
def load(self, context, name, namespace, data):
|
||||||
"""
|
"""
|
||||||
Loading function to get the soft effects to particular read node
|
Loading function to get the soft effects to particular read node
|
||||||
|
|
@ -8,8 +8,8 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import lib
|
from ayon_nuke.api import lib
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
|
|
@ -23,6 +23,8 @@ class LoadEffectsInputProcess(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"json"}
|
extensions = {"json"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Effects - Input Process"
|
label = "Load Effects - Input Process"
|
||||||
order = 0
|
order = 0
|
||||||
icon = "eye"
|
icon = "eye"
|
||||||
|
|
@ -5,13 +5,13 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
maintained_selection,
|
maintained_selection,
|
||||||
get_avalon_knob_data,
|
get_avalon_knob_data,
|
||||||
set_avalon_knob_data,
|
set_avalon_knob_data,
|
||||||
swap_node_with_dependency,
|
swap_node_with_dependency,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
|
|
@ -25,6 +25,8 @@ class LoadGizmo(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"nk"}
|
extensions = {"nk"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Gizmo"
|
label = "Load Gizmo"
|
||||||
order = 0
|
order = 0
|
||||||
icon = "dropbox"
|
icon = "dropbox"
|
||||||
|
|
@ -6,14 +6,14 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
maintained_selection,
|
maintained_selection,
|
||||||
create_backdrop,
|
create_backdrop,
|
||||||
get_avalon_knob_data,
|
get_avalon_knob_data,
|
||||||
set_avalon_knob_data,
|
set_avalon_knob_data,
|
||||||
swap_node_with_dependency,
|
swap_node_with_dependency,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
|
|
@ -27,6 +27,8 @@ class LoadGizmoInputProcess(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"nk"}
|
extensions = {"nk"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Gizmo - Input Process"
|
label = "Load Gizmo - Input Process"
|
||||||
order = 0
|
order = 0
|
||||||
icon = "eye"
|
icon = "eye"
|
||||||
|
|
@ -7,10 +7,10 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
get_imageio_input_colorspace
|
get_imageio_input_colorspace
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
|
|
@ -33,9 +33,9 @@ class LoadImage(load.LoaderPlugin):
|
||||||
"image",
|
"image",
|
||||||
}
|
}
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = set(
|
extensions = set(ext.lstrip(".") for ext in IMAGE_EXTENSIONS)
|
||||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS
|
|
||||||
)
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Image"
|
label = "Load Image"
|
||||||
order = -10
|
order = -10
|
||||||
|
|
@ -11,6 +11,8 @@ class MatchmoveLoader(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"py"}
|
extensions = {"py"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
defaults = ["Camera", "Object"]
|
defaults = ["Camera", "Object"]
|
||||||
|
|
||||||
label = "Run matchmove script"
|
label = "Run matchmove script"
|
||||||
|
|
@ -5,8 +5,8 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import maintained_selection
|
from ayon_nuke.api.lib import maintained_selection
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
|
|
@ -22,6 +22,8 @@ class AlembicModelLoader(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"abc"}
|
extensions = {"abc"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Alembic"
|
label = "Load Alembic"
|
||||||
icon = "cube"
|
icon = "cube"
|
||||||
color = "orange"
|
color = "orange"
|
||||||
|
|
@ -10,7 +10,7 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
viewer_update_and_undo_stop,
|
viewer_update_and_undo_stop,
|
||||||
update_container,
|
update_container,
|
||||||
|
|
@ -24,6 +24,8 @@ class LoadOcioLookNodes(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"json"}
|
extensions = {"json"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load OcioLook [nodes]"
|
label = "Load OcioLook [nodes]"
|
||||||
order = 0
|
order = 0
|
||||||
icon = "cc"
|
icon = "cc"
|
||||||
|
|
@ -5,8 +5,8 @@ from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
get_representation_path,
|
get_representation_path,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.nuke.api.lib import get_avalon_knob_data
|
from ayon_nuke.api.lib import get_avalon_knob_data
|
||||||
from ayon_core.hosts.nuke.api import (
|
from ayon_nuke.api import (
|
||||||
containerise,
|
containerise,
|
||||||
update_container,
|
update_container,
|
||||||
viewer_update_and_undo_stop
|
viewer_update_and_undo_stop
|
||||||
|
|
@ -20,6 +20,8 @@ class LinkAsGroup(load.LoaderPlugin):
|
||||||
representations = {"*"}
|
representations = {"*"}
|
||||||
extensions = {"nk"}
|
extensions = {"nk"}
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
label = "Load Precomp"
|
label = "Load Precomp"
|
||||||
order = 0
|
order = 0
|
||||||
icon = "file"
|
icon = "file"
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
from ayon_core.hosts.nuke.api import lib as pnlib
|
from ayon_nuke.api import lib as pnlib
|
||||||
import nuke
|
import nuke
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -13,6 +13,8 @@ class CollectBackdrops(pyblish.api.InstancePlugin):
|
||||||
hosts = ["nuke"]
|
hosts = ["nuke"]
|
||||||
families = ["nukenodes"]
|
families = ["nukenodes"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
self.log.debug(pformat(instance.data))
|
self.log.debug(pformat(instance.data))
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@ import os
|
||||||
import nuke
|
import nuke
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
from ayon_core.lib import get_version_from_path
|
from ayon_core.lib import get_version_from_path
|
||||||
import ayon_core.hosts.nuke.api as napi
|
import ayon_nuke.api as napi
|
||||||
from ayon_core.pipeline import KnownPublishError
|
from ayon_core.pipeline import KnownPublishError
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -13,6 +13,8 @@ class CollectContextData(pyblish.api.ContextPlugin):
|
||||||
label = "Collect context data"
|
label = "Collect context data"
|
||||||
hosts = ['nuke']
|
hosts = ['nuke']
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, context): # sourcery skip: avoid-builtin-shadow
|
def process(self, context): # sourcery skip: avoid-builtin-shadow
|
||||||
root_node = nuke.root()
|
root_node = nuke.root()
|
||||||
|
|
||||||
|
|
@ -13,5 +13,7 @@ class CollectFramerate(pyblish.api.ContextPlugin):
|
||||||
"nukeassist"
|
"nukeassist"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
context.data["fps"] = nuke.root()["fps"].getValue()
|
context.data["fps"] = nuke.root()["fps"].getValue()
|
||||||
|
|
@ -11,6 +11,8 @@ class CollectGizmo(pyblish.api.InstancePlugin):
|
||||||
hosts = ["nuke"]
|
hosts = ["nuke"]
|
||||||
families = ["gizmo"]
|
families = ["gizmo"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
|
|
||||||
gizmo_node = instance.data["transientData"]["node"]
|
gizmo_node = instance.data["transientData"]["node"]
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
from ayon_core.pipeline.publish import (
|
||||||
|
AYONPyblishPluginMixin
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CollectRenderOnFarm(pyblish.api.ContextPlugin):
|
||||||
|
"""Setup instances for render on farm submission."""
|
||||||
|
|
||||||
|
# Needs to be after CollectFromCreateContext
|
||||||
|
order = pyblish.api.CollectorOrder - 0.49
|
||||||
|
label = "Collect Render On Farm"
|
||||||
|
hosts = ["nuke"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
|
def process(self, context):
|
||||||
|
if not context.data.get("render_on_farm", False):
|
||||||
|
return
|
||||||
|
|
||||||
|
for instance in context:
|
||||||
|
if instance.data["family"] == "workfile":
|
||||||
|
instance.data["active"] = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Filter out all other instances.
|
||||||
|
node = instance.data["transientData"]["node"]
|
||||||
|
if node.name() != instance.context.data["node_name"]:
|
||||||
|
instance.data["active"] = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
instance.data["families"].append("render_on_farm")
|
||||||
|
|
||||||
|
# Enable for farm publishing.
|
||||||
|
instance.data["farm"] = True
|
||||||
|
|
||||||
|
# Skip workfile version incremental save.
|
||||||
|
instance.context.data["increment_script_version"] = False
|
||||||
|
|
||||||
|
|
||||||
|
class SetupRenderOnFarm(pyblish.api.InstancePlugin, AYONPyblishPluginMixin):
|
||||||
|
"""Setup instance for render on farm submission."""
|
||||||
|
|
||||||
|
order = pyblish.api.CollectorOrder + 0.4999
|
||||||
|
label = "Setup Render On Farm"
|
||||||
|
hosts = ["nuke"]
|
||||||
|
families = ["render_on_farm"]
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
# Clear the families as we only want the main family, ei. no review
|
||||||
|
# etc.
|
||||||
|
instance.data["families"] = ["render_on_farm"]
|
||||||
|
|
||||||
|
# Use the workfile instead of published.
|
||||||
|
publish_attributes = instance.data["publish_attributes"]
|
||||||
|
plugin_attributes = publish_attributes["NukeSubmitDeadline"]
|
||||||
|
plugin_attributes["use_published_workfile"] = False
|
||||||
|
|
@ -11,6 +11,8 @@ class CollectModel(pyblish.api.InstancePlugin):
|
||||||
hosts = ["nuke"]
|
hosts = ["nuke"]
|
||||||
families = ["model"]
|
families = ["model"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
|
|
||||||
geo_node = instance.data["transientData"]["node"]
|
geo_node = instance.data["transientData"]["node"]
|
||||||
|
|
@ -11,6 +11,8 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
|
||||||
label = "Collect Nuke Instance Data"
|
label = "Collect Nuke Instance Data"
|
||||||
hosts = ["nuke", "nukeassist"]
|
hosts = ["nuke", "nukeassist"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
# presets
|
# presets
|
||||||
sync_workfile_version_on_families = []
|
sync_workfile_version_on_families = []
|
||||||
|
|
||||||
|
|
@ -12,6 +12,8 @@ class CollectNukeReads(pyblish.api.InstancePlugin):
|
||||||
hosts = ["nuke", "nukeassist"]
|
hosts = ["nuke", "nukeassist"]
|
||||||
families = ["source"]
|
families = ["source"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
self.log.debug("checking instance: {}".format(instance))
|
self.log.debug("checking instance: {}".format(instance))
|
||||||
|
|
||||||
|
|
@ -10,6 +10,8 @@ class CollectSlate(pyblish.api.InstancePlugin):
|
||||||
hosts = ["nuke"]
|
hosts = ["nuke"]
|
||||||
families = ["render"]
|
families = ["render"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
node = instance.data["transientData"]["node"]
|
node = instance.data["transientData"]["node"]
|
||||||
|
|
||||||
|
|
@ -11,6 +11,8 @@ class CollectWorkfile(pyblish.api.InstancePlugin):
|
||||||
hosts = ['nuke']
|
hosts = ['nuke']
|
||||||
families = ["workfile"]
|
families = ["workfile"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance): # sourcery skip: avoid-builtin-shadow
|
def process(self, instance): # sourcery skip: avoid-builtin-shadow
|
||||||
|
|
||||||
script_data = instance.context.data["scriptData"]
|
script_data = instance.context.data["scriptData"]
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import nuke
|
import nuke
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
from ayon_core.hosts.nuke import api as napi
|
from ayon_nuke import api as napi
|
||||||
from ayon_core.pipeline import publish
|
from ayon_core.pipeline import publish
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -14,6 +14,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin,
|
||||||
hosts = ["nuke", "nukeassist"]
|
hosts = ["nuke", "nukeassist"]
|
||||||
families = ["render", "prerender", "image"]
|
families = ["render", "prerender", "image"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
# cache
|
# cache
|
||||||
_write_nodes = {}
|
_write_nodes = {}
|
||||||
_frame_ranges = {}
|
_frame_ranges = {}
|
||||||
|
|
@ -5,7 +5,7 @@ import nuke
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import publish
|
from ayon_core.pipeline import publish
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
maintained_selection,
|
maintained_selection,
|
||||||
reset_selection,
|
reset_selection,
|
||||||
select_nodes
|
select_nodes
|
||||||
|
|
@ -25,6 +25,8 @@ class ExtractBackdropNode(publish.Extractor):
|
||||||
hosts = ["nuke"]
|
hosts = ["nuke"]
|
||||||
families = ["nukenodes"]
|
families = ["nukenodes"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
tmp_nodes = []
|
tmp_nodes = []
|
||||||
child_nodes = instance.data["transientData"]["childNodes"]
|
child_nodes = instance.data["transientData"]["childNodes"]
|
||||||
|
|
@ -6,7 +6,7 @@ import nuke
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import publish
|
from ayon_core.pipeline import publish
|
||||||
from ayon_core.hosts.nuke.api.lib import maintained_selection
|
from ayon_nuke.api.lib import maintained_selection
|
||||||
|
|
||||||
|
|
||||||
class ExtractCamera(publish.Extractor):
|
class ExtractCamera(publish.Extractor):
|
||||||
|
|
@ -17,6 +17,8 @@ class ExtractCamera(publish.Extractor):
|
||||||
families = ["camera"]
|
families = ["camera"]
|
||||||
hosts = ["nuke"]
|
hosts = ["nuke"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
# presets
|
# presets
|
||||||
write_geo_knobs = [
|
write_geo_knobs = [
|
||||||
("file_type", "abc"),
|
("file_type", "abc"),
|
||||||
|
|
@ -4,8 +4,8 @@ import nuke
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import publish
|
from ayon_core.pipeline import publish
|
||||||
from ayon_core.hosts.nuke.api import utils as pnutils
|
from ayon_nuke.api import utils as pnutils
|
||||||
from ayon_core.hosts.nuke.api.lib import (
|
from ayon_nuke.api.lib import (
|
||||||
maintained_selection,
|
maintained_selection,
|
||||||
reset_selection,
|
reset_selection,
|
||||||
select_nodes
|
select_nodes
|
||||||
|
|
@ -23,6 +23,8 @@ class ExtractGizmo(publish.Extractor):
|
||||||
hosts = ["nuke"]
|
hosts = ["nuke"]
|
||||||
families = ["gizmo"]
|
families = ["gizmo"]
|
||||||
|
|
||||||
|
settings_category = "nuke"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
tmp_nodes = []
|
tmp_nodes = []
|
||||||
orig_grpn = instance.data["transientData"]["node"]
|
orig_grpn = instance.data["transientData"]["node"]
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue