mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
Merge branch 'develop' into enhancement/AY-974_Nuke-simplified-deadline-submission-on-write-node
This commit is contained in:
commit
8eb5c2a99e
96 changed files with 607 additions and 106 deletions
|
|
@ -51,6 +51,8 @@ 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),
|
||||||
|
"tvpaint": VersionInfo(0, 2, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Inherit from `object` for Python 2 hosts
|
# Inherit from `object` for Python 2 hosts
|
||||||
|
|
|
||||||
|
|
@ -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",
|
|
||||||
)
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ from aiohttp_json_rpc.protocol import (
|
||||||
from aiohttp_json_rpc.exceptions import RpcError
|
from aiohttp_json_rpc.exceptions import RpcError
|
||||||
|
|
||||||
from ayon_core.lib import emit_event
|
from ayon_core.lib import emit_event
|
||||||
from ayon_core.hosts.tvpaint.tvpaint_plugin import get_plugin_files_path
|
from ayon_tvpaint.tvpaint_plugin import get_plugin_files_path
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
|
|
@ -10,7 +10,7 @@ from qtpy import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
from ayon_core import style
|
from ayon_core import style
|
||||||
from ayon_core.pipeline import install_host
|
from ayon_core.pipeline import install_host
|
||||||
from ayon_core.hosts.tvpaint.api import (
|
from ayon_tvpaint.api import (
|
||||||
TVPaintHost,
|
TVPaintHost,
|
||||||
CommunicationWrapper,
|
CommunicationWrapper,
|
||||||
)
|
)
|
||||||
|
|
@ -7,8 +7,9 @@ import requests
|
||||||
import ayon_api
|
import ayon_api
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
|
from ayon_tvpaint import TVPAINT_ROOT_DIR
|
||||||
|
|
||||||
from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost
|
from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost
|
||||||
from ayon_core.hosts.tvpaint import TVPAINT_ROOT_DIR
|
|
||||||
from ayon_core.settings import get_current_project_settings
|
from ayon_core.settings import get_current_project_settings
|
||||||
from ayon_core.lib import register_event_callback
|
from ayon_core.lib import register_event_callback
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
|
|
@ -12,7 +12,7 @@ from ayon_core.pipeline.create.creator_plugins import cache_and_get_instances
|
||||||
from .lib import get_layers_data
|
from .lib import get_layers_data
|
||||||
|
|
||||||
|
|
||||||
SHARED_DATA_KEY = "openpype.tvpaint.instances"
|
SHARED_DATA_KEY = "ayon.tvpaint.instances"
|
||||||
|
|
||||||
|
|
||||||
class TVPaintCreatorCommon:
|
class TVPaintCreatorCommon:
|
||||||
|
|
@ -89,6 +89,8 @@ class TVPaintCreatorCommon:
|
||||||
|
|
||||||
|
|
||||||
class TVPaintCreator(Creator, TVPaintCreatorCommon):
|
class TVPaintCreator(Creator, TVPaintCreatorCommon):
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def collect_instances(self):
|
def collect_instances(self):
|
||||||
self._collect_create_instances()
|
self._collect_create_instances()
|
||||||
|
|
||||||
|
|
@ -140,6 +142,8 @@ class TVPaintCreator(Creator, TVPaintCreatorCommon):
|
||||||
|
|
||||||
|
|
||||||
class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon):
|
class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon):
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def collect_instances(self):
|
def collect_instances(self):
|
||||||
self._collect_create_instances()
|
self._collect_create_instances()
|
||||||
|
|
||||||
|
|
@ -152,6 +156,7 @@ class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon):
|
||||||
|
|
||||||
class Loader(LoaderPlugin):
|
class Loader(LoaderPlugin):
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_members_from_container(container):
|
def get_members_from_container(container):
|
||||||
|
|
@ -37,6 +37,6 @@ class TvpaintPrelaunchHook(PreLaunchHook):
|
||||||
self.launch_context.launch_args.extend(remainders)
|
self.launch_context.launch_args.extend(remainders)
|
||||||
|
|
||||||
def launch_script_path(self):
|
def launch_script_path(self):
|
||||||
from ayon_core.hosts.tvpaint import get_launch_script_path
|
from ayon_tvpaint import get_launch_script_path
|
||||||
|
|
||||||
return get_launch_script_path()
|
return get_launch_script_path()
|
||||||
|
|
@ -4,8 +4,8 @@ from ayon_core.pipeline.create.creator_plugins import (
|
||||||
ProductConvertorPlugin,
|
ProductConvertorPlugin,
|
||||||
cache_and_get_instances,
|
cache_and_get_instances,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.plugin import SHARED_DATA_KEY
|
from ayon_tvpaint.api.plugin import SHARED_DATA_KEY
|
||||||
from ayon_core.hosts.tvpaint.api.lib import get_groups_data
|
from ayon_tvpaint.api.lib import get_groups_data
|
||||||
|
|
||||||
|
|
||||||
class TVPaintLegacyConverted(ProductConvertorPlugin):
|
class TVPaintLegacyConverted(ProductConvertorPlugin):
|
||||||
|
|
@ -52,11 +52,11 @@ from ayon_core.pipeline.create import (
|
||||||
CreatedInstance,
|
CreatedInstance,
|
||||||
CreatorError,
|
CreatorError,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.plugin import (
|
from ayon_tvpaint.api.plugin import (
|
||||||
TVPaintCreator,
|
TVPaintCreator,
|
||||||
TVPaintAutoCreator,
|
TVPaintAutoCreator,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_tvpaint.api.lib import (
|
||||||
get_layers_data,
|
get_layers_data,
|
||||||
get_groups_data,
|
get_groups_data,
|
||||||
execute_george_through_file,
|
execute_george_through_file,
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import ayon_api
|
import ayon_api
|
||||||
|
|
||||||
from ayon_core.pipeline import CreatedInstance
|
from ayon_core.pipeline import CreatedInstance
|
||||||
from ayon_core.hosts.tvpaint.api.plugin import TVPaintAutoCreator
|
from ayon_tvpaint.api.plugin import TVPaintAutoCreator
|
||||||
|
|
||||||
|
|
||||||
class TVPaintReviewCreator(TVPaintAutoCreator):
|
class TVPaintReviewCreator(TVPaintAutoCreator):
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import ayon_api
|
import ayon_api
|
||||||
|
|
||||||
from ayon_core.pipeline import CreatedInstance
|
from ayon_core.pipeline import CreatedInstance
|
||||||
from ayon_core.hosts.tvpaint.api.plugin import TVPaintAutoCreator
|
from ayon_tvpaint.api.plugin import TVPaintAutoCreator
|
||||||
|
|
||||||
|
|
||||||
class TVPaintWorkfileCreator(TVPaintAutoCreator):
|
class TVPaintWorkfileCreator(TVPaintAutoCreator):
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from ayon_core.lib.attribute_definitions import BoolDef
|
from ayon_core.lib.attribute_definitions import BoolDef
|
||||||
from ayon_core.hosts.tvpaint.api import plugin
|
from ayon_tvpaint.api import plugin
|
||||||
from ayon_core.hosts.tvpaint.api.lib import execute_george_through_file
|
from ayon_tvpaint.api.lib import execute_george_through_file
|
||||||
|
|
||||||
|
|
||||||
class ImportImage(plugin.Loader):
|
class ImportImage(plugin.Loader):
|
||||||
|
|
@ -2,12 +2,12 @@ import collections
|
||||||
|
|
||||||
from ayon_core.lib.attribute_definitions import BoolDef
|
from ayon_core.lib.attribute_definitions import BoolDef
|
||||||
from ayon_core.pipeline import registered_host
|
from ayon_core.pipeline import registered_host
|
||||||
from ayon_core.hosts.tvpaint.api import plugin
|
from ayon_tvpaint.api import plugin
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_tvpaint.api.lib import (
|
||||||
get_layers_data,
|
get_layers_data,
|
||||||
execute_george_through_file,
|
execute_george_through_file,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.pipeline import (
|
from ayon_tvpaint.api.pipeline import (
|
||||||
write_workfile_metadata,
|
write_workfile_metadata,
|
||||||
SECTION_NAME_CONTAINERS,
|
SECTION_NAME_CONTAINERS,
|
||||||
containerise,
|
containerise,
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from ayon_core.hosts.tvpaint.api import plugin
|
from ayon_tvpaint.api import plugin
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_tvpaint.api.lib import (
|
||||||
execute_george_through_file,
|
execute_george_through_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -10,11 +10,11 @@ from ayon_core.pipeline.workfile import (
|
||||||
get_last_workfile_with_version,
|
get_last_workfile_with_version,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline.template_data import get_template_data_with_names
|
from ayon_core.pipeline.template_data import get_template_data_with_names
|
||||||
from ayon_core.hosts.tvpaint.api import plugin
|
from ayon_tvpaint.api import plugin
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_tvpaint.api.lib import (
|
||||||
execute_george_through_file,
|
execute_george_through_file,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.pipeline import (
|
from ayon_tvpaint.api.pipeline import (
|
||||||
get_current_workfile_context,
|
get_current_workfile_context,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline.version_start import get_versioning_start
|
from ayon_core.pipeline.version_start import get_versioning_start
|
||||||
|
|
@ -14,6 +14,8 @@ class CollectOutputFrameRange(pyblish.api.InstancePlugin):
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
families = ["review", "render"]
|
families = ["review", "render"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
folder_entity = instance.data.get("folderEntity")
|
folder_entity = instance.data.get("folderEntity")
|
||||||
if not folder_entity:
|
if not folder_entity:
|
||||||
|
|
@ -9,6 +9,7 @@ class CollectRenderInstances(pyblish.api.InstancePlugin):
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
families = ["render", "review"]
|
families = ["render", "review"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
ignore_render_pass_transparency = False
|
ignore_render_pass_transparency = False
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
|
|
@ -9,6 +9,8 @@ class CollectWorkfile(pyblish.api.InstancePlugin):
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
families = ["workfile"]
|
families = ["workfile"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
context = instance.context
|
context = instance.context
|
||||||
current_file = context.data["currentFile"]
|
current_file = context.data["currentFile"]
|
||||||
|
|
@ -4,13 +4,13 @@ import tempfile
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_tvpaint.api.lib import (
|
||||||
execute_george,
|
execute_george,
|
||||||
execute_george_through_file,
|
execute_george_through_file,
|
||||||
get_layers_data,
|
get_layers_data,
|
||||||
get_groups_data,
|
get_groups_data,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.pipeline import (
|
from ayon_tvpaint.api.pipeline import (
|
||||||
SECTION_NAME_CONTEXT,
|
SECTION_NAME_CONTEXT,
|
||||||
SECTION_NAME_INSTANCES,
|
SECTION_NAME_INSTANCES,
|
||||||
SECTION_NAME_CONTAINERS,
|
SECTION_NAME_CONTAINERS,
|
||||||
|
|
@ -58,6 +58,8 @@ class CollectWorkfileData(pyblish.api.ContextPlugin):
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
actions = [ResetTVPaintWorkfileMetadata]
|
actions = [ResetTVPaintWorkfileMetadata]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
current_project_id = execute_george("tv_projectcurrentid")
|
current_project_id = execute_george("tv_projectcurrentid")
|
||||||
execute_george("tv_projectselect {}".format(current_project_id))
|
execute_george("tv_projectselect {}".format(current_project_id))
|
||||||
|
|
@ -23,6 +23,8 @@ class ExtractConvertToEXR(pyblish.api.InstancePlugin):
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
families = ["render"]
|
families = ["render"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
enabled = False
|
enabled = False
|
||||||
|
|
||||||
# Replace source PNG files or just add
|
# Replace source PNG files or just add
|
||||||
|
|
@ -10,13 +10,13 @@ from ayon_core.pipeline.publish import (
|
||||||
KnownPublishError,
|
KnownPublishError,
|
||||||
get_publish_instance_families,
|
get_publish_instance_families,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_tvpaint.api.lib import (
|
||||||
execute_george,
|
execute_george,
|
||||||
execute_george_through_file,
|
execute_george_through_file,
|
||||||
get_layers_pre_post_behavior,
|
get_layers_pre_post_behavior,
|
||||||
get_layers_exposure_frames,
|
get_layers_exposure_frames,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.lib import (
|
from ayon_tvpaint.lib import (
|
||||||
calculate_layers_extraction_data,
|
calculate_layers_extraction_data,
|
||||||
get_frame_filename_template,
|
get_frame_filename_template,
|
||||||
fill_reference_frames,
|
fill_reference_frames,
|
||||||
|
|
@ -31,6 +31,8 @@ class ExtractSequence(pyblish.api.InstancePlugin):
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
families = ["review", "render"]
|
families = ["review", "render"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
# Modifiable with settings
|
# Modifiable with settings
|
||||||
review_bg = [255, 255, 255, 1.0]
|
review_bg = [255, 255, 255, 1.0]
|
||||||
|
|
||||||
|
|
@ -12,6 +12,8 @@ class IncrementWorkfileVersion(pyblish.api.ContextPlugin):
|
||||||
optional = True
|
optional = True
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
|
|
||||||
assert all(result["success"] for result in context.data["results"]), (
|
assert all(result["success"] for result in context.data["results"]), (
|
||||||
|
|
@ -3,7 +3,7 @@ from ayon_core.pipeline import (
|
||||||
PublishXmlValidationError,
|
PublishXmlValidationError,
|
||||||
OptionalPyblishPluginMixin,
|
OptionalPyblishPluginMixin,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.pipeline import (
|
from ayon_tvpaint.api.pipeline import (
|
||||||
list_instances,
|
list_instances,
|
||||||
write_instances,
|
write_instances,
|
||||||
)
|
)
|
||||||
|
|
@ -48,6 +48,8 @@ class ValidateAssetName(
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
actions = [FixFolderPaths]
|
actions = [FixFolderPaths]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
if not self.is_active(context.data):
|
if not self.is_active(context.data):
|
||||||
return
|
return
|
||||||
|
|
@ -9,6 +9,8 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin):
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
families = ["renderPass"]
|
families = ["renderPass"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
# Prepare layers
|
# Prepare layers
|
||||||
layers_by_name = instance.context.data["layersByName"]
|
layers_by_name = instance.context.data["layersByName"]
|
||||||
|
|
@ -10,6 +10,8 @@ class ValidateLayersVisiblity(pyblish.api.InstancePlugin):
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
families = ["review", "render"]
|
families = ["review", "render"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
layers = instance.data.get("layers")
|
layers = instance.data.get("layers")
|
||||||
# Instance have empty layers
|
# Instance have empty layers
|
||||||
|
|
@ -5,7 +5,7 @@ from ayon_core.pipeline import (
|
||||||
PublishXmlValidationError,
|
PublishXmlValidationError,
|
||||||
OptionalPyblishPluginMixin,
|
OptionalPyblishPluginMixin,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.lib import execute_george
|
from ayon_tvpaint.api.lib import execute_george
|
||||||
|
|
||||||
|
|
||||||
class ValidateMarksRepair(pyblish.api.Action):
|
class ValidateMarksRepair(pyblish.api.Action):
|
||||||
|
|
@ -41,6 +41,8 @@ class ValidateMarks(
|
||||||
optional = True
|
optional = True
|
||||||
actions = [ValidateMarksRepair]
|
actions = [ValidateMarksRepair]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_expected_data(context):
|
def get_expected_data(context):
|
||||||
scene_mark_in = context.data["sceneMarkIn"]
|
scene_mark_in = context.data["sceneMarkIn"]
|
||||||
|
|
@ -9,6 +9,8 @@ class ValidateMissingLayers(pyblish.api.InstancePlugin):
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
families = ["renderPass"]
|
families = ["renderPass"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
# Prepare layers
|
# Prepare layers
|
||||||
layers_by_name = instance.context.data["layersByName"]
|
layers_by_name = instance.context.data["layersByName"]
|
||||||
|
|
@ -12,6 +12,8 @@ class ValidateRenderLayerGroups(pyblish.api.ContextPlugin):
|
||||||
label = "Validate Render Layers Group"
|
label = "Validate Render Layers Group"
|
||||||
order = pyblish.api.ValidatorOrder + 0.1
|
order = pyblish.api.ValidatorOrder + 0.1
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
# Prepare layers
|
# Prepare layers
|
||||||
render_layers_by_group_id = collections.defaultdict(list)
|
render_layers_by_group_id = collections.defaultdict(list)
|
||||||
|
|
@ -13,6 +13,8 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin):
|
||||||
order = pyblish.api.ValidatorOrder + 0.1
|
order = pyblish.api.ValidatorOrder + 0.1
|
||||||
families = ["renderPass"]
|
families = ["renderPass"]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
# Prepare layers
|
# Prepare layers
|
||||||
layers_data = instance.context.data["layersData"]
|
layers_data = instance.context.data["layersData"]
|
||||||
|
|
@ -16,6 +16,8 @@ class ValidateProjectSettings(
|
||||||
|
|
||||||
label = "Validate Scene Settings"
|
label = "Validate Scene Settings"
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
optional = True
|
optional = True
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
|
|
@ -3,7 +3,7 @@ from ayon_core.pipeline import (
|
||||||
PublishXmlValidationError,
|
PublishXmlValidationError,
|
||||||
OptionalPyblishPluginMixin,
|
OptionalPyblishPluginMixin,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.tvpaint.api.lib import execute_george
|
from ayon_tvpaint.api.lib import execute_george
|
||||||
|
|
||||||
|
|
||||||
class RepairStartFrame(pyblish.api.Action):
|
class RepairStartFrame(pyblish.api.Action):
|
||||||
|
|
@ -27,6 +27,8 @@ class ValidateStartFrame(
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
hosts = ["tvpaint"]
|
hosts = ["tvpaint"]
|
||||||
actions = [RepairStartFrame]
|
actions = [RepairStartFrame]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
optional = True
|
optional = True
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
|
|
@ -31,6 +31,8 @@ class ValidateWorkfileMetadata(pyblish.api.ContextPlugin):
|
||||||
|
|
||||||
actions = [ValidateWorkfileMetadataRepair]
|
actions = [ValidateWorkfileMetadataRepair]
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
required_keys = {"project_name", "folder_path", "task_name"}
|
required_keys = {"project_name", "folder_path", "task_name"}
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
|
|
@ -12,6 +12,8 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin):
|
||||||
label = "Validate Workfile Project Name"
|
label = "Validate Workfile Project Name"
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
|
|
||||||
|
settings_category = "tvpaint"
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
workfile_context = context.data.get("workfile_context")
|
workfile_context = context.data.get("workfile_context")
|
||||||
# If workfile context is missing than project is matching to
|
# If workfile context is missing than project is matching to
|
||||||
|
|
@ -5,11 +5,11 @@ import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from ayon_core.hosts.tvpaint.api.communication_server import (
|
from ayon_tvpaint.api.communication_server import (
|
||||||
BaseCommunicator,
|
BaseCommunicator,
|
||||||
CommunicationWrapper
|
CommunicationWrapper
|
||||||
)
|
)
|
||||||
from openpype_modules.job_queue.job_workers import WorkerJobsConnection
|
from ayon_core.modules.job_queue.job_workers import WorkerJobsConnection
|
||||||
|
|
||||||
from .worker_job import ProcessTVPaintCommands
|
from .worker_job import ProcessTVPaintCommands
|
||||||
|
|
||||||
|
|
@ -256,7 +256,7 @@ class CollectSceneData(BaseCommand):
|
||||||
name = "collect_scene_data"
|
name = "collect_scene_data"
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_tvpaint.api.lib import (
|
||||||
get_layers_data,
|
get_layers_data,
|
||||||
get_groups_data,
|
get_groups_data,
|
||||||
get_layers_pre_post_behavior,
|
get_layers_pre_post_behavior,
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
name = "tvpaint"
|
name = "tvpaint"
|
||||||
title = "TVPaint"
|
title = "TVPaint"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
|
client_dir = "ayon_tvpaint"
|
||||||
|
|
||||||
|
ayon_required_addons = {
|
||||||
|
"core": ">0.3.2",
|
||||||
|
}
|
||||||
|
ayon_compatible_addons = {}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue