diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py
index 21b1193b07..13f1550e6e 100644
--- a/client/ayon_core/addon/base.py
+++ b/client/ayon_core/addon/base.py
@@ -8,7 +8,6 @@ import inspect
import logging
import threading
import collections
-
from uuid import uuid4
from abc import ABCMeta, abstractmethod
@@ -51,8 +50,22 @@ IGNORED_MODULES_IN_AYON = set()
# - this is used to log the missing addon
MOVED_ADDON_MILESTONE_VERSIONS = {
"applications": VersionInfo(0, 2, 0),
+ "celaction": VersionInfo(0, 2, 0),
+ "clockify": VersionInfo(0, 2, 0),
+ "flame": VersionInfo(0, 2, 0),
+ "fusion": VersionInfo(0, 2, 0),
+ "max": VersionInfo(0, 2, 0),
+ "photoshop": VersionInfo(0, 2, 0),
+ "traypublisher": VersionInfo(0, 2, 0),
+ "tvpaint": VersionInfo(0, 2, 0),
+ "maya": VersionInfo(0, 2, 0),
+ "nuke": VersionInfo(0, 2, 0),
+ "resolve": VersionInfo(0, 2, 0),
+ "substancepainter": VersionInfo(0, 2, 0),
+ "houdini": VersionInfo(0, 3, 0),
}
+
# Inherit from `object` for Python 2 hosts
class _ModuleClass(object):
"""Fake module class for storing AYON addons.
@@ -538,6 +551,9 @@ class AYONAddon(object):
enabled = True
_id = None
+ # Temporary variable for 'version' property
+ _missing_version_warned = False
+
def __init__(self, manager, settings):
self.manager = manager
@@ -568,6 +584,26 @@ class AYONAddon(object):
pass
+ @property
+ def version(self):
+ """Addon version.
+
+ Todo:
+ Should be abstract property (required). Introduced in
+ ayon-core 0.3.3 .
+
+ Returns:
+ str: Addon version as semver compatible string.
+
+ """
+ if not self.__class__._missing_version_warned:
+ self.__class__._missing_version_warned = True
+ print(
+ f"DEV WARNING: Addon '{self.name}' does not have"
+ f" defined version."
+ )
+ return "0.0.0"
+
def initialize(self, settings):
"""Initialization of addon attributes.
@@ -683,6 +719,30 @@ class OpenPypeAddOn(OpenPypeModule):
enabled = True
+class _AddonReportInfo:
+ def __init__(
+ self, class_name, name, version, report_value_by_label
+ ):
+ self.class_name = class_name
+ self.name = name
+ self.version = version
+ self.report_value_by_label = report_value_by_label
+
+ @classmethod
+ def from_addon(cls, addon, report):
+ class_name = addon.__class__.__name__
+ report_value_by_label = {
+ label: reported.get(class_name)
+ for label, reported in report.items()
+ }
+ return cls(
+ addon.__class__.__name__,
+ addon.name,
+ addon.version,
+ report_value_by_label
+ )
+
+
class AddonsManager:
"""Manager of addons that helps to load and prepare them to work.
@@ -859,10 +919,6 @@ class AddonsManager:
name_alias = getattr(addon, "openpype_alias", None)
if name_alias:
aliased_names.append((name_alias, addon))
- enabled_str = "X"
- if not addon.enabled:
- enabled_str = " "
- self.log.debug("[{}] {}".format(enabled_str, name))
now = time.time()
report[addon.__class__.__name__] = now - prev_start_time
@@ -874,6 +930,13 @@ class AddonsManager:
exc_info=True
)
+ for addon_name in sorted(self._addons_by_name.keys()):
+ addon = self._addons_by_name[addon_name]
+ enabled_str = "X" if addon.enabled else " "
+ self.log.debug(
+ f"[{enabled_str}] {addon.name} ({addon.version})"
+ )
+
for item in aliased_names:
name_alias, addon = item
if name_alias not in self._addons_by_name:
@@ -1162,39 +1225,55 @@ class AddonsManager:
available_col_names |= set(addon_names.keys())
# Prepare ordered dictionary for columns
- cols = collections.OrderedDict()
- # Add addon names to first columnt
- cols["Addon name"] = list(sorted(
- addon.__class__.__name__
+ addons_info = [
+ _AddonReportInfo.from_addon(addon, self._report)
for addon in self.addons
if addon.__class__.__name__ in available_col_names
- ))
+ ]
+ addons_info.sort(key=lambda x: x.name)
+
+ addon_name_rows = [
+ addon_info.name
+ for addon_info in addons_info
+ ]
+ addon_version_rows = [
+ addon_info.version
+ for addon_info in addons_info
+ ]
+
# Add total key (as last addon)
- cols["Addon name"].append(self._report_total_key)
+ addon_name_rows.append(self._report_total_key)
+ addon_version_rows.append(f"({len(addons_info)})")
+
+ cols = collections.OrderedDict()
+ # Add addon names to first columnt
+ cols["Addon name"] = addon_name_rows
+ cols["Version"] = addon_version_rows
# Add columns from report
+ total_by_addon = {
+ row: 0
+ for row in addon_name_rows
+ }
for label in self._report.keys():
- cols[label] = []
-
- total_addon_times = {}
- for addon_name in cols["Addon name"]:
- total_addon_times[addon_name] = 0
-
- for label, reported in self._report.items():
- for addon_name in cols["Addon name"]:
- col_time = reported.get(addon_name)
- if col_time is None:
- cols[label].append("N/A")
+ rows = []
+ col_total = 0
+ for addon_info in addons_info:
+ value = addon_info.report_value_by_label.get(label)
+ if value is None:
+ rows.append("N/A")
continue
- cols[label].append("{:.3f}".format(col_time))
- total_addon_times[addon_name] += col_time
-
+ rows.append("{:.3f}".format(value))
+ total_by_addon[addon_info.name] += value
+ col_total += value
+ total_by_addon[self._report_total_key] += col_total
+ rows.append("{:.3f}".format(col_total))
+ cols[label] = rows
# Add to also total column that should sum the row
- cols[self._report_total_key] = []
- for addon_name in cols["Addon name"]:
- cols[self._report_total_key].append(
- "{:.3f}".format(total_addon_times[addon_name])
- )
+ cols[self._report_total_key] = [
+ "{:.3f}".format(total_by_addon[addon_name])
+ for addon_name in cols["Addon name"]
+ ]
# Prepare column widths and total row count
# - column width is by
@@ -1321,7 +1400,7 @@ class TrayAddonsManager(AddonsManager):
self.doubleclick_callback = None
def add_doubleclick_callback(self, addon, callback):
- """Register doubleclick callbacks on tray icon.
+ """Register double-click callbacks on tray icon.
Currently, there is no way how to determine which is launched. Name of
callback can be defined with `doubleclick_callback` attribute.
diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py
index 0817afec71..6c30b267bc 100644
--- a/client/ayon_core/hooks/pre_ocio_hook.py
+++ b/client/ayon_core/hooks/pre_ocio_hook.py
@@ -1,7 +1,7 @@
from ayon_applications import PreLaunchHook
-from ayon_core.pipeline.colorspace import get_imageio_config
-from ayon_core.pipeline.template_data import get_template_data_with_names
+from ayon_core.pipeline.colorspace import get_imageio_config_preset
+from ayon_core.pipeline.template_data import get_template_data
class OCIOEnvHook(PreLaunchHook):
@@ -26,32 +26,38 @@ class OCIOEnvHook(PreLaunchHook):
def execute(self):
"""Hook entry method."""
- template_data = get_template_data_with_names(
- project_name=self.data["project_name"],
- folder_path=self.data["folder_path"],
- task_name=self.data["task_name"],
+ folder_entity = self.data["folder_entity"]
+
+ template_data = get_template_data(
+ self.data["project_entity"],
+ folder_entity=folder_entity,
+ task_entity=self.data["task_entity"],
host_name=self.host_name,
- settings=self.data["project_settings"]
+ settings=self.data["project_settings"],
)
- config_data = get_imageio_config(
- project_name=self.data["project_name"],
- host_name=self.host_name,
- project_settings=self.data["project_settings"],
- anatomy_data=template_data,
+ config_data = get_imageio_config_preset(
+ self.data["project_name"],
+ self.data["folder_path"],
+ self.data["task_name"],
+ self.host_name,
anatomy=self.data["anatomy"],
+ project_settings=self.data["project_settings"],
+ template_data=template_data,
env=self.launch_context.env,
+ folder_id=folder_entity["id"],
)
- if config_data:
- ocio_path = config_data["path"]
-
- if self.host_name in ["nuke", "hiero"]:
- ocio_path = ocio_path.replace("\\", "/")
-
- self.log.info(
- f"Setting OCIO environment to config path: {ocio_path}")
-
- self.launch_context.env["OCIO"] = ocio_path
- else:
+ if not config_data:
self.log.debug("OCIO not set or enabled")
+ return
+
+ ocio_path = config_data["path"]
+
+ if self.host_name in ["nuke", "hiero"]:
+ ocio_path = ocio_path.replace("\\", "/")
+
+ self.log.info(
+ f"Setting OCIO environment to config path: {ocio_path}")
+
+ self.launch_context.env["OCIO"] = ocio_path
diff --git a/client/ayon_core/hosts/aftereffects/api/launch_logic.py b/client/ayon_core/hosts/aftereffects/api/launch_logic.py
index 5a23f2cb35..da6887668a 100644
--- a/client/ayon_core/hosts/aftereffects/api/launch_logic.py
+++ b/client/ayon_core/hosts/aftereffects/api/launch_logic.py
@@ -60,7 +60,7 @@ def main(*subprocess_args):
)
)
- elif os.environ.get("AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH", True):
+ elif os.environ.get("AVALON_AFTEREFFECTS_WORKFILES_ON_LAUNCH", True):
save = False
if os.getenv("WORKFILES_SAVE_AS"):
save = True
diff --git a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py
index c28042b6ae..ebd4b8f944 100644
--- a/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py
+++ b/client/ayon_core/hosts/aftereffects/plugins/publish/collect_render.py
@@ -24,7 +24,7 @@ class AERenderInstance(RenderInstance):
class CollectAERender(publish.AbstractCollectRender):
- order = pyblish.api.CollectorOrder + 0.405
+ order = pyblish.api.CollectorOrder + 0.100
label = "Collect After Effects Render Layers"
hosts = ["aftereffects"]
@@ -145,6 +145,7 @@ class CollectAERender(publish.AbstractCollectRender):
if "review" in instance.families:
# to skip ExtractReview locally
instance.families.remove("review")
+ instance.deadline = inst.data.get("deadline")
instances.append(instance)
diff --git a/client/ayon_core/hosts/blender/api/lib.py b/client/ayon_core/hosts/blender/api/lib.py
index 458a275b51..ea37f17d05 100644
--- a/client/ayon_core/hosts/blender/api/lib.py
+++ b/client/ayon_core/hosts/blender/api/lib.py
@@ -33,7 +33,7 @@ def load_scripts(paths):
if register:
try:
register()
- except:
+ except: # noqa E722
traceback.print_exc()
else:
print("\nWarning! '%s' has no register function, "
@@ -45,7 +45,7 @@ def load_scripts(paths):
if unregister:
try:
unregister()
- except:
+ except: # noqa E722
traceback.print_exc()
def test_reload(mod):
@@ -57,7 +57,7 @@ def load_scripts(paths):
try:
return importlib.reload(mod)
- except:
+ except: # noqa E722
traceback.print_exc()
def test_register(mod):
@@ -365,3 +365,62 @@ def maintained_time():
yield
finally:
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]
diff --git a/client/ayon_core/hosts/blender/api/plugin.py b/client/ayon_core/hosts/blender/api/plugin.py
index 6c9bfb6569..e00e127f70 100644
--- a/client/ayon_core/hosts/blender/api/plugin.py
+++ b/client/ayon_core/hosts/blender/api/plugin.py
@@ -26,7 +26,8 @@ from .ops import (
)
from .lib import imprint
-VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx"]
+VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx",
+ ".usd", ".usdc", ".usda"]
def prepare_scene_name(
@@ -143,13 +144,19 @@ def deselect_all():
if obj.mode != 'OBJECT':
modes.append((obj, obj.mode))
bpy.context.view_layer.objects.active = obj
- bpy.ops.object.mode_set(mode='OBJECT')
+ context_override = create_blender_context(active=obj)
+ with bpy.context.temp_override(**context_override):
+ bpy.ops.object.mode_set(mode='OBJECT')
- bpy.ops.object.select_all(action='DESELECT')
+ context_override = create_blender_context()
+ with bpy.context.temp_override(**context_override):
+ bpy.ops.object.select_all(action='DESELECT')
for p in modes:
bpy.context.view_layer.objects.active = p[0]
- bpy.ops.object.mode_set(mode=p[1])
+ context_override = create_blender_context(active=p[0])
+ with bpy.context.temp_override(**context_override):
+ bpy.ops.object.mode_set(mode=p[1])
bpy.context.view_layer.objects.active = active
diff --git a/client/ayon_core/hosts/blender/plugins/create/create_usd.py b/client/ayon_core/hosts/blender/plugins/create/create_usd.py
new file mode 100644
index 0000000000..2c2d0c46c6
--- /dev/null
+++ b/client/ayon_core/hosts/blender/plugins/create/create_usd.py
@@ -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
diff --git a/client/ayon_core/hosts/blender/plugins/load/load_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_cache.py
similarity index 94%
rename from client/ayon_core/hosts/blender/plugins/load/load_abc.py
rename to client/ayon_core/hosts/blender/plugins/load/load_cache.py
index c074b5ed13..30c847f89d 100644
--- a/client/ayon_core/hosts/blender/plugins/load/load_abc.py
+++ b/client/ayon_core/hosts/blender/plugins/load/load_cache.py
@@ -26,10 +26,10 @@ class CacheModelLoader(plugin.AssetLoader):
Note:
At least for now it only supports Alembic files.
"""
- product_types = {"model", "pointcache", "animation"}
- representations = {"abc"}
+ product_types = {"model", "pointcache", "animation", "usd"}
+ representations = {"abc", "usd"}
- label = "Load Alembic"
+ label = "Load Cache"
icon = "code-fork"
color = "orange"
@@ -53,10 +53,21 @@ class CacheModelLoader(plugin.AssetLoader):
plugin.deselect_all()
relative = bpy.context.preferences.filepaths.use_relative_paths
- bpy.ops.wm.alembic_import(
- filepath=libpath,
- relative_path=relative
- )
+
+ if any(libpath.lower().endswith(ext)
+ 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()
diff --git a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py
index 6178578081..a49bb40d9a 100644
--- a/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py
+++ b/client/ayon_core/hosts/blender/plugins/load/load_camera_abc.py
@@ -43,7 +43,10 @@ class AbcCameraLoader(plugin.AssetLoader):
def _process(self, libpath, asset_group, group_name):
plugin.deselect_all()
- bpy.ops.wm.alembic_import(filepath=libpath)
+ # Force the creation of the transform cache even if the camera
+ # doesn't have an animation. We use the cache to update the camera.
+ bpy.ops.wm.alembic_import(
+ filepath=libpath, always_add_cache_reader=True)
objects = lib.get_selection()
@@ -178,12 +181,33 @@ class AbcCameraLoader(plugin.AssetLoader):
self.log.info("Library already loaded, not updating...")
return
- mat = asset_group.matrix_basis.copy()
+ for obj in asset_group.children:
+ found = False
+ for constraint in obj.constraints:
+ if constraint.type == "TRANSFORM_CACHE":
+ constraint.cache_file.filepath = libpath.as_posix()
+ found = True
+ break
+ if not found:
+ # This is to keep compatibility with cameras loaded with
+ # the old loader
+ # Create a new constraint for the cache file
+ constraint = obj.constraints.new("TRANSFORM_CACHE")
+ bpy.ops.cachefile.open(filepath=libpath.as_posix())
+ constraint.cache_file = bpy.data.cache_files[-1]
+ constraint.cache_file.scale = 1.0
- self._remove(asset_group)
- self._process(str(libpath), asset_group, object_name)
+ # This is a workaround to set the object path. Blender doesn't
+ # load the list of object paths until the object is evaluated.
+ # This is a hack to force the object to be evaluated.
+ # The modifier doesn't need to be removed because camera
+ # objects don't have modifiers.
+ obj.modifiers.new(
+ name='MeshSequenceCache', type='MESH_SEQUENCE_CACHE')
+ bpy.context.evaluated_depsgraph_get()
- asset_group.matrix_basis = mat
+ constraint.object_path = (
+ constraint.cache_file.object_paths[0].path)
metadata["libpath"] = str(libpath)
metadata["representation"] = repre_entity["id"]
diff --git a/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py b/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py
index d47c69a270..314ffd368a 100644
--- a/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py
+++ b/client/ayon_core/hosts/blender/plugins/publish/collect_instance.py
@@ -12,7 +12,7 @@ class CollectBlenderInstanceData(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder
hosts = ["blender"]
families = ["model", "pointcache", "animation", "rig", "camera", "layout",
- "blendScene"]
+ "blendScene", "usd"]
label = "Collect Instance"
def process(self, instance):
diff --git a/client/ayon_core/hosts/blender/plugins/publish/extract_usd.py b/client/ayon_core/hosts/blender/plugins/publish/extract_usd.py
new file mode 100644
index 0000000000..1d4fa3d7ac
--- /dev/null
+++ b/client/ayon_core/hosts/blender/plugins/publish/extract_usd.py
@@ -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
diff --git a/client/ayon_core/hosts/flame/__init__.py b/client/ayon_core/hosts/flame/__init__.py
deleted file mode 100644
index b45f107747..0000000000
--- a/client/ayon_core/hosts/flame/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from .addon import (
- HOST_DIR,
- FlameAddon,
-)
-
-
-__all__ = (
- "HOST_DIR",
- "FlameAddon",
-)
diff --git a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py
index 156e2ac6ba..c63eb114e5 100644
--- a/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py
+++ b/client/ayon_core/hosts/harmony/plugins/publish/collect_farm_render.py
@@ -177,7 +177,10 @@ class CollectFarmRender(publish.AbstractCollectRender):
outputFormat=info[1],
outputStartFrame=info[3],
leadingZeros=info[2],
- ignoreFrameHandleCheck=True
+ ignoreFrameHandleCheck=True,
+ #todo: inst is not available, must be determined, fix when
+ #reworking to Publisher
+ # deadline=inst.data.get("deadline")
)
render_instance.context = context
diff --git a/client/ayon_core/hosts/hiero/api/lib.py b/client/ayon_core/hosts/hiero/api/lib.py
index aaf99546c7..456a68f125 100644
--- a/client/ayon_core/hosts/hiero/api/lib.py
+++ b/client/ayon_core/hosts/hiero/api/lib.py
@@ -1110,10 +1110,7 @@ def apply_colorspace_project():
'''
# backward compatibility layer
# TODO: remove this after some time
- config_data = get_imageio_config(
- project_name=get_current_project_name(),
- host_name="hiero"
- )
+ config_data = get_current_context_imageio_config_preset()
if config_data:
presets.update({
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_pointcache_type.py b/client/ayon_core/hosts/houdini/plugins/publish/collect_pointcache_type.py
deleted file mode 100644
index 3323e97c20..0000000000
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_pointcache_type.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Collector for pointcache types.
-
-This will add additional family to pointcache instance based on
-the creator_identifier parameter.
-"""
-import pyblish.api
-
-
-class CollectPointcacheType(pyblish.api.InstancePlugin):
- """Collect data type for pointcache instance."""
-
- order = pyblish.api.CollectorOrder
- hosts = ["houdini"]
- families = ["pointcache"]
- label = "Collect type of pointcache"
-
- def process(self, instance):
- if instance.data["creator_identifier"] == "io.openpype.creators.houdini.bgeo": # noqa: E501
- instance.data["families"] += ["bgeo"]
- elif instance.data["creator_identifier"] == "io.openpype.creators.houdini.pointcache": # noqa: E501
- instance.data["families"] += ["abc"]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py
deleted file mode 100644
index 5197100406..0000000000
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import pyblish.api
-
-from ayon_core.pipeline import OptionalPyblishPluginMixin
-from ayon_core.pipeline.publish import RepairAction, PublishValidationError
-
-
-class ValidateAlembicDefaultsPointcache(
- pyblish.api.InstancePlugin, OptionalPyblishPluginMixin
-):
- """Validate the attributes on the instance are defaults.
-
- The defaults are defined in the project settings.
- """
-
- order = pyblish.api.ValidatorOrder
- families = ["pointcache"]
- hosts = ["maya"]
- label = "Validate Alembic Options Defaults"
- actions = [RepairAction]
- optional = True
-
- plugin_name = "ExtractAlembic"
-
- @classmethod
- def _get_settings(cls, context):
- maya_settings = context.data["project_settings"]["maya"]
- settings = maya_settings["publish"]["ExtractAlembic"]
- return settings
-
- @classmethod
- def _get_publish_attributes(cls, instance):
- attributes = instance.data["publish_attributes"][
- cls.plugin_name(
- instance.data["publish_attributes"]
- )
- ]
-
- return attributes
-
- def process(self, instance):
- if not self.is_active(instance.data):
- return
-
- settings = self._get_settings(instance.context)
-
- attributes = self._get_publish_attributes(instance)
-
- msg = (
- "Alembic Extract setting \"{}\" is not the default value:"
- "\nCurrent: {}"
- "\nDefault Value: {}\n"
- )
- errors = []
- for key, value in attributes.items():
- default_value = settings[key]
-
- # Lists are best to compared sorted since we cant rely on the order
- # of the items.
- if isinstance(value, list):
- value = sorted(value)
- default_value = sorted(default_value)
-
- if value != default_value:
- errors.append(msg.format(key, value, default_value))
-
- if errors:
- raise PublishValidationError("\n".join(errors))
-
- @classmethod
- def repair(cls, instance):
- # Find create instance twin.
- create_context = instance.context.data["create_context"]
- create_instance = create_context.get_instance_by_id(
- instance.data["instance_id"]
- )
-
- # Set the settings values on the create context then save to workfile.
- publish_attributes = instance.data["publish_attributes"]
- plugin_name = cls.plugin_name(publish_attributes)
- attributes = cls._get_publish_attributes(instance)
- settings = cls._get_settings(instance.context)
- create_publish_attributes = create_instance.data["publish_attributes"]
- for key in attributes:
- create_publish_attributes[plugin_name][key] = settings[key]
-
- create_context.save_changes()
-
-
-class ValidateAlembicDefaultsAnimation(
- ValidateAlembicDefaultsPointcache
-):
- """Validate the attributes on the instance are defaults.
-
- The defaults are defined in the project settings.
- """
- label = "Validate Alembic Options Defaults"
- families = ["animation"]
- plugin_name = "ExtractAnimation"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py
deleted file mode 100644
index 2ba2bff6fc..0000000000
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- PublishValidationError,
- ValidateContentsOrder,
- OptionalPyblishPluginMixin
-)
-from maya import cmds
-
-
-class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin,
- OptionalPyblishPluginMixin):
- """Validate all nodes in skeletonAnim_SET are referenced"""
-
- order = ValidateContentsOrder
- hosts = ["maya"]
- families = ["animation.fbx"]
- label = "Animated Reference Rig"
- accepted_controllers = ["transform", "locator"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
- optional = False
-
- def process(self, instance):
- if not self.is_active(instance.data):
- return
- animated_sets = instance.data.get("animated_skeleton", [])
- if not animated_sets:
- self.log.debug(
- "No nodes found in skeletonAnim_SET. "
- "Skipping validation of animated reference rig..."
- )
- return
-
- for animated_reference in animated_sets:
- is_referenced = cmds.referenceQuery(
- animated_reference, isNodeReferenced=True)
- if not bool(is_referenced):
- raise PublishValidationError(
- "All the content in skeletonAnim_SET"
- " should be referenced nodes"
- )
- invalid_controls = self.validate_controls(animated_sets)
- if invalid_controls:
- raise PublishValidationError(
- "All the content in skeletonAnim_SET"
- " should be transforms"
- )
-
- @classmethod
- def validate_controls(self, set_members):
- """Check if the controller set contains only accepted node types.
-
- Checks if all its set members are within the hierarchy of the root
- Checks if the node types of the set members valid
-
- Args:
- set_members: list of nodes of the skeleton_anim_set
- hierarchy: list of nodes which reside under the root node
-
- Returns:
- errors (list)
- """
-
- # Validate control types
- invalid = []
- set_members = cmds.ls(set_members, long=True)
- for node in set_members:
- if cmds.nodeType(node) not in self.accepted_controllers:
- invalid.append(node)
-
- return invalid
diff --git a/client/ayon_core/hosts/resolve/__init__.py b/client/ayon_core/hosts/resolve/__init__.py
deleted file mode 100644
index b4a994bbaa..0000000000
--- a/client/ayon_core/hosts/resolve/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from .addon import ResolveAddon
-
-
-__all__ = (
- "ResolveAddon",
-)
diff --git a/client/ayon_core/hosts/resolve/otio/__init__.py b/client/ayon_core/hosts/resolve/otio/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/client/ayon_core/hosts/unreal/lib.py b/client/ayon_core/hosts/unreal/lib.py
index 37122b2096..185853a0aa 100644
--- a/client/ayon_core/hosts/unreal/lib.py
+++ b/client/ayon_core/hosts/unreal/lib.py
@@ -80,17 +80,21 @@ def get_engine_versions(env=None):
def get_editor_exe_path(engine_path: Path, engine_version: str) -> Path:
"""Get UE Editor executable path."""
ue_path = engine_path / "Engine/Binaries"
+
+ ue_name = "UnrealEditor"
+
+ # handle older versions of Unreal Engine
+ if engine_version.split(".")[0] == "4":
+ ue_name = "UE4Editor"
+
if platform.system().lower() == "windows":
- if engine_version.split(".")[0] == "4":
- ue_path /= "Win64/UE4Editor.exe"
- elif engine_version.split(".")[0] == "5":
- ue_path /= "Win64/UnrealEditor.exe"
+ ue_path /= f"Win64/{ue_name}.exe"
elif platform.system().lower() == "linux":
- ue_path /= "Linux/UE4Editor"
+ ue_path /= f"Linux/{ue_name}"
elif platform.system().lower() == "darwin":
- ue_path /= "Mac/UE4Editor"
+ ue_path /= f"Mac/{ue_name}"
return ue_path
diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py
index e436396c6c..e25d3479ee 100644
--- a/client/ayon_core/lib/__init__.py
+++ b/client/ayon_core/lib/__init__.py
@@ -139,6 +139,7 @@ from .path_tools import (
)
from .ayon_info import (
+ is_in_ayon_launcher_process,
is_running_from_build,
is_using_ayon_console,
is_staging_enabled,
@@ -248,6 +249,7 @@ __all__ = [
"Logger",
+ "is_in_ayon_launcher_process",
"is_running_from_build",
"is_using_ayon_console",
"is_staging_enabled",
diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py
index fc09a7c90c..c4333fab95 100644
--- a/client/ayon_core/lib/ayon_info.py
+++ b/client/ayon_core/lib/ayon_info.py
@@ -1,4 +1,5 @@
import os
+import sys
import json
import datetime
import platform
@@ -25,6 +26,18 @@ def get_ayon_launcher_version():
return content["__version__"]
+def is_in_ayon_launcher_process():
+ """Determine if current process is running from AYON launcher.
+
+ Returns:
+ bool: True if running from AYON launcher.
+
+ """
+ ayon_executable_path = os.path.normpath(os.environ["AYON_EXECUTABLE"])
+ executable_path = os.path.normpath(sys.executable)
+ return ayon_executable_path == executable_path
+
+
def is_running_from_build():
"""Determine if current process is running from build or code.
diff --git a/client/ayon_core/modules/clockify/__init__.py b/client/ayon_core/modules/clockify/__init__.py
deleted file mode 100644
index 98834b516c..0000000000
--- a/client/ayon_core/modules/clockify/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from .clockify_module import ClockifyModule
-
-__all__ = (
- "ClockifyModule",
-)
diff --git a/client/ayon_core/modules/deadline/__init__.py b/client/ayon_core/modules/deadline/__init__.py
index 5631e501d8..683d8dbe4a 100644
--- a/client/ayon_core/modules/deadline/__init__.py
+++ b/client/ayon_core/modules/deadline/__init__.py
@@ -1,6 +1,8 @@
from .deadline_module import DeadlineModule
+from .version import __version__
__all__ = (
"DeadlineModule",
+ "__version__"
)
diff --git a/client/ayon_core/modules/deadline/abstract_submit_deadline.py b/client/ayon_core/modules/deadline/abstract_submit_deadline.py
index 2e0518ae20..564966b6a0 100644
--- a/client/ayon_core/modules/deadline/abstract_submit_deadline.py
+++ b/client/ayon_core/modules/deadline/abstract_submit_deadline.py
@@ -29,15 +29,11 @@ from ayon_core.pipeline.publish.lib import (
JSONDecodeError = getattr(json.decoder, "JSONDecodeError", ValueError)
-# TODO both 'requests_post' and 'requests_get' should not set 'verify' based
-# on environment variable. This should be done in a more controlled way,
-# e.g. each deadline url could have checkbox to enabled/disable
-# ssl verification.
def requests_post(*args, **kwargs):
"""Wrap request post method.
- Disabling SSL certificate validation if ``DONT_VERIFY_SSL`` environment
- variable is found. This is useful when Deadline server is
+ Disabling SSL certificate validation if ``verify`` kwarg is set to False.
+ This is useful when Deadline server is
running with self-signed certificates and its certificate is not
added to trusted certificates on client machines.
@@ -46,9 +42,9 @@ def requests_post(*args, **kwargs):
of defense SSL is providing, and it is not recommended.
"""
- if 'verify' not in kwargs:
- kwargs['verify'] = False if os.getenv("OPENPYPE_DONT_VERIFY_SSL",
- True) else True # noqa
+ auth = kwargs.get("auth")
+ if auth:
+ kwargs["auth"] = tuple(auth) # explicit cast to tuple
# add 10sec timeout before bailing out
kwargs['timeout'] = 10
return requests.post(*args, **kwargs)
@@ -57,8 +53,8 @@ def requests_post(*args, **kwargs):
def requests_get(*args, **kwargs):
"""Wrap request get method.
- Disabling SSL certificate validation if ``DONT_VERIFY_SSL`` environment
- variable is found. This is useful when Deadline server is
+ Disabling SSL certificate validation if ``verify`` kwarg is set to False.
+ This is useful when Deadline server is
running with self-signed certificates and its certificate is not
added to trusted certificates on client machines.
@@ -67,9 +63,9 @@ def requests_get(*args, **kwargs):
of defense SSL is providing, and it is not recommended.
"""
- if 'verify' not in kwargs:
- kwargs['verify'] = False if os.getenv("OPENPYPE_DONT_VERIFY_SSL",
- True) else True # noqa
+ auth = kwargs.get("auth")
+ if auth:
+ kwargs["auth"] = tuple(auth)
# add 10sec timeout before bailing out
kwargs['timeout'] = 10
return requests.get(*args, **kwargs)
@@ -434,9 +430,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
"""Plugin entry point."""
self._instance = instance
context = instance.context
- self._deadline_url = context.data.get("defaultDeadline")
- self._deadline_url = instance.data.get(
- "deadlineUrl", self._deadline_url)
+ self._deadline_url = instance.data["deadline"]["url"]
assert self._deadline_url, "Requires Deadline Webservice URL"
@@ -460,7 +454,9 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
self.plugin_info = self.get_plugin_info()
self.aux_files = self.get_aux_files()
- job_id = self.process_submission()
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
+ job_id = self.process_submission(auth, verify)
self.log.info("Submitted job to Deadline: {}.".format(job_id))
# TODO: Find a way that's more generic and not render type specific
@@ -473,10 +469,10 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
job_info=render_job_info,
plugin_info=render_plugin_info
)
- render_job_id = self.submit(payload)
+ render_job_id = self.submit(payload, auth, verify)
self.log.info("Render job id: %s", render_job_id)
- def process_submission(self):
+ def process_submission(self, auth=None, verify=True):
"""Process data for submission.
This takes Deadline JobInfo, PluginInfo, AuxFile, creates payload
@@ -487,7 +483,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
"""
payload = self.assemble_payload()
- return self.submit(payload)
+ return self.submit(payload, auth, verify)
@abstractmethod
def get_job_info(self):
@@ -577,7 +573,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
"AuxFiles": aux_files or self.aux_files
}
- def submit(self, payload):
+ def submit(self, payload, auth, verify):
"""Submit payload to Deadline API end-point.
This takes payload in the form of JSON file and POST it to
@@ -585,6 +581,8 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
Args:
payload (dict): dict to become json in deadline submission.
+ auth (tuple): (username, password)
+ verify (bool): verify SSL certificate if present
Returns:
str: resulting Deadline job id.
@@ -594,7 +592,8 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
"""
url = "{}/api/jobs".format(self._deadline_url)
- response = requests_post(url, json=payload)
+ response = requests_post(
+ url, json=payload, auth=auth, verify=verify)
if not response.ok:
self.log.error("Submission failed!")
self.log.error(response.status_code)
diff --git a/client/ayon_core/modules/deadline/deadline_module.py b/client/ayon_core/modules/deadline/deadline_module.py
index c0ba83477e..ea0350d2d9 100644
--- a/client/ayon_core/modules/deadline/deadline_module.py
+++ b/client/ayon_core/modules/deadline/deadline_module.py
@@ -7,6 +7,8 @@ import six
from ayon_core.lib import Logger
from ayon_core.modules import AYONAddon, IPluginPaths
+from .version import __version__
+
class DeadlineWebserviceError(Exception):
"""
@@ -16,26 +18,27 @@ class DeadlineWebserviceError(Exception):
class DeadlineModule(AYONAddon, IPluginPaths):
name = "deadline"
+ version = __version__
def initialize(self, studio_settings):
# This module is always enabled
- deadline_urls = {}
+ deadline_servers_info = {}
enabled = self.name in studio_settings
if enabled:
deadline_settings = studio_settings[self.name]
- deadline_urls = {
- url_item["name"]: url_item["value"]
+ deadline_servers_info = {
+ url_item["name"]: url_item
for url_item in deadline_settings["deadline_urls"]
}
- if enabled and not deadline_urls:
+ if enabled and not deadline_servers_info:
enabled = False
self.log.warning((
"Deadline Webservice URLs are not specified. Disabling addon."
))
self.enabled = enabled
- self.deadline_urls = deadline_urls
+ self.deadline_servers_info = deadline_servers_info
def get_plugin_paths(self):
"""Deadline plugin paths."""
@@ -45,13 +48,15 @@ class DeadlineModule(AYONAddon, IPluginPaths):
}
@staticmethod
- def get_deadline_pools(webservice, log=None):
+ def get_deadline_pools(webservice, auth=None, log=None):
"""Get pools from Deadline.
Args:
webservice (str): Server url.
- log (Logger)
+ auth (Optional[Tuple[str, str]]): Tuple containing username,
+ password
+ log (Optional[Logger]): Logger to log errors to, if provided.
Returns:
- list: Pools.
+ List[str]: Pools.
Throws:
RuntimeError: If deadline webservice is unreachable.
@@ -63,7 +68,10 @@ class DeadlineModule(AYONAddon, IPluginPaths):
argument = "{}/api/pools?NamesOnly=true".format(webservice)
try:
- response = requests_get(argument)
+ kwargs = {}
+ if auth:
+ kwargs["auth"] = auth
+ response = requests_get(argument, **kwargs)
except requests.exceptions.ConnectionError as exc:
msg = 'Cannot connect to DL web service {}'.format(webservice)
log.error(msg)
diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py b/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py
index ea4b7a213e..22022831a0 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/collect_deadline_server_from_instance.py
@@ -13,17 +13,45 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin):
"""Collect Deadline Webservice URL from instance."""
# Run before collect_render.
- order = pyblish.api.CollectorOrder + 0.005
+ order = pyblish.api.CollectorOrder + 0.225
label = "Deadline Webservice from the Instance"
- families = ["rendering", "renderlayer"]
- hosts = ["maya"]
+ targets = ["local"]
+ families = ["render",
+ "rendering",
+ "render.farm",
+ "renderFarm",
+ "renderlayer",
+ "maxrender",
+ "usdrender",
+ "redshift_rop",
+ "arnold_rop",
+ "mantra_rop",
+ "karma_rop",
+ "vray_rop",
+ "publish.hou",
+ "image"] # for Fusion
def process(self, instance):
- instance.data["deadlineUrl"] = self._collect_deadline_url(instance)
- instance.data["deadlineUrl"] = \
- instance.data["deadlineUrl"].strip().rstrip("/")
+ if not instance.data.get("farm"):
+ self.log.debug("Should not be processed on farm, skipping.")
+ return
+
+ if not instance.data.get("deadline"):
+ instance.data["deadline"] = {}
+
+ # todo: separate logic should be removed, all hosts should have same
+ host_name = instance.context.data["hostName"]
+ if host_name == "maya":
+ deadline_url = self._collect_deadline_url(instance)
+ else:
+ deadline_url = (instance.data.get("deadlineUrl") or # backwards
+ instance.data.get("deadline", {}).get("url"))
+ if deadline_url:
+ instance.data["deadline"]["url"] = deadline_url.strip().rstrip("/")
+ else:
+ instance.data["deadline"]["url"] = instance.context.data["deadline"]["defaultUrl"] # noqa
self.log.debug(
- "Using {} for submission.".format(instance.data["deadlineUrl"]))
+ "Using {} for submission".format(instance.data["deadline"]["url"]))
def _collect_deadline_url(self, render_instance):
# type: (pyblish.api.Instance) -> str
@@ -49,13 +77,13 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin):
["project_settings"]
["deadline"]
)
-
- default_server = render_instance.context.data["defaultDeadline"]
+ default_server_url = (render_instance.context.data["deadline"]
+ ["defaultUrl"])
# QUESTION How and where is this is set? Should be removed?
instance_server = render_instance.data.get("deadlineServers")
if not instance_server:
self.log.debug("Using default server.")
- return default_server
+ return default_server_url
# Get instance server as sting.
if isinstance(instance_server, int):
@@ -66,7 +94,7 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin):
default_servers = {
url_item["name"]: url_item["value"]
- for url_item in deadline_settings["deadline_urls"]
+ for url_item in deadline_settings["deadline_servers_info"]
}
project_servers = (
render_instance.context.data
diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_default_deadline_server.py b/client/ayon_core/modules/deadline/plugins/publish/collect_default_deadline_server.py
index b7ca227b01..9238e0ed95 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/collect_default_deadline_server.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/collect_default_deadline_server.py
@@ -18,10 +18,9 @@ class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
"""
# Run before collect_deadline_server_instance.
- order = pyblish.api.CollectorOrder + 0.0025
+ order = pyblish.api.CollectorOrder + 0.200
label = "Default Deadline Webservice"
-
- pass_mongo_url = False
+ targets = ["local"]
def process(self, context):
try:
@@ -33,15 +32,17 @@ class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
deadline_settings = context.data["project_settings"]["deadline"]
deadline_server_name = deadline_settings["deadline_server"]
- deadline_webservice = None
+ dl_server_info = None
if deadline_server_name:
- deadline_webservice = deadline_module.deadline_urls.get(
+ dl_server_info = deadline_module.deadline_servers_info.get(
deadline_server_name)
- default_deadline_webservice = deadline_module.deadline_urls["default"]
- deadline_webservice = (
- deadline_webservice
- or default_deadline_webservice
- )
+ if dl_server_info:
+ deadline_url = dl_server_info["value"]
+ else:
+ default_dl_server_info = deadline_module.deadline_servers_info[0]
+ deadline_url = default_dl_server_info["value"]
- context.data["defaultDeadline"] = deadline_webservice.strip().rstrip("/") # noqa
+ context.data["deadline"] = {}
+ context.data["deadline"]["defaultUrl"] = (
+ deadline_url.strip().rstrip("/"))
diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py
index 6923c2b16b..2592d358e5 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/collect_pools.py
@@ -26,27 +26,32 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin,
order = pyblish.api.CollectorOrder + 0.420
label = "Collect Deadline Pools"
- hosts = ["aftereffects",
- "fusion",
- "harmony"
- "nuke",
- "maya",
- "max",
- "houdini"]
+ hosts = [
+ "aftereffects",
+ "fusion",
+ "harmony",
+ "maya",
+ "max",
+ "houdini",
+ "nuke",
+ ]
- families = ["render",
- "rendering",
- "render.farm",
- "renderFarm",
- "renderlayer",
- "maxrender",
- "usdrender",
- "redshift_rop",
- "arnold_rop",
- "mantra_rop",
- "karma_rop",
- "vray_rop",
- "publish.hou"]
+ families = [
+ "render",
+ "prerender",
+ "rendering",
+ "render.farm",
+ "renderFarm",
+ "renderlayer",
+ "maxrender",
+ "usdrender",
+ "redshift_rop",
+ "arnold_rop",
+ "mantra_rop",
+ "karma_rop",
+ "vray_rop",
+ "publish.hou",
+ ]
primary_pool = None
secondary_pool = None
diff --git a/client/ayon_core/modules/deadline/plugins/publish/collect_user_credentials.py b/client/ayon_core/modules/deadline/plugins/publish/collect_user_credentials.py
new file mode 100644
index 0000000000..99d75ecb9e
--- /dev/null
+++ b/client/ayon_core/modules/deadline/plugins/publish/collect_user_credentials.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+"""Collect user credentials
+
+Requires:
+ context -> project_settings
+ instance.data["deadline"]["url"]
+
+Provides:
+ instance.data["deadline"] -> require_authentication (bool)
+ instance.data["deadline"] -> auth (tuple (str, str)) -
+ (username, password) or None
+"""
+import pyblish.api
+
+from ayon_api import get_server_api_connection
+from ayon_core.modules.deadline.deadline_module import DeadlineModule
+from ayon_core.modules.deadline import __version__
+
+
+class CollectDeadlineUserCredentials(pyblish.api.InstancePlugin):
+ """Collects user name and password for artist if DL requires authentication
+ """
+ order = pyblish.api.CollectorOrder + 0.250
+ label = "Collect Deadline User Credentials"
+
+ targets = ["local"]
+ hosts = ["aftereffects",
+ "blender",
+ "fusion",
+ "harmony",
+ "nuke",
+ "maya",
+ "max",
+ "houdini"]
+
+ families = ["render",
+ "rendering",
+ "render.farm",
+ "renderFarm",
+ "renderlayer",
+ "maxrender",
+ "usdrender",
+ "redshift_rop",
+ "arnold_rop",
+ "mantra_rop",
+ "karma_rop",
+ "vray_rop",
+ "publish.hou"]
+
+ def process(self, instance):
+ if not instance.data.get("farm"):
+ self.log.debug("Should not be processed on farm, skipping.")
+ return
+
+ collected_deadline_url = instance.data["deadline"]["url"]
+ if not collected_deadline_url:
+ raise ValueError("Instance doesn't have '[deadline][url]'.")
+ context_data = instance.context.data
+ deadline_settings = context_data["project_settings"]["deadline"]
+
+ deadline_server_name = None
+ # deadline url might be set directly from instance, need to find
+ # metadata for it
+ for deadline_info in deadline_settings["deadline_urls"]:
+ dl_settings_url = deadline_info["value"].strip().rstrip("/")
+ if dl_settings_url == collected_deadline_url:
+ deadline_server_name = deadline_info["name"]
+ break
+
+ if not deadline_server_name:
+ raise ValueError(f"Collected {collected_deadline_url} doesn't "
+ "match any site configured in Studio Settings")
+
+ instance.data["deadline"]["require_authentication"] = (
+ deadline_info["require_authentication"]
+ )
+ instance.data["deadline"]["auth"] = None
+
+ instance.data["deadline"]["verify"] = (
+ not deadline_info["not_verify_ssl"])
+
+ if not deadline_info["require_authentication"]:
+ return
+ # TODO import 'get_addon_site_settings' when available
+ # in public 'ayon_api'
+ local_settings = get_server_api_connection().get_addon_site_settings(
+ DeadlineModule.name, __version__)
+ local_settings = local_settings["local_settings"]
+ for server_info in local_settings:
+ if deadline_server_name == server_info["server_name"]:
+ instance.data["deadline"]["auth"] = (server_info["username"],
+ server_info["password"])
diff --git a/client/ayon_core/modules/deadline/plugins/publish/help/validate_deadline_connection.xml b/client/ayon_core/modules/deadline/plugins/publish/help/validate_deadline_connection.xml
new file mode 100644
index 0000000000..eec05df08a
--- /dev/null
+++ b/client/ayon_core/modules/deadline/plugins/publish/help/validate_deadline_connection.xml
@@ -0,0 +1,17 @@
+
+
+
+ Deadline Authentication
+
+## Deadline authentication is required
+
+This project has set in Settings that Deadline requires authentication.
+
+### How to repair?
+
+Please go to Ayon Server > Site Settings and provide your Deadline username and password.
+In some cases the password may be empty if Deadline is configured to allow that. Ask your administrator.
+
+
+
+
\ No newline at end of file
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py
index ab342c1a9d..311dbcedd5 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py
@@ -174,7 +174,9 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
instance.data["toBeRenderedOn"] = "deadline"
payload = self.assemble_payload()
- return self.submit(payload)
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
+ return self.submit(payload, auth=auth, verify=verify)
def from_published_scene(self):
"""
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py
index 1fae23c9b2..a17bf0c3ef 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_celaction_deadline.py
@@ -2,9 +2,10 @@ import os
import re
import json
import getpass
-import requests
import pyblish.api
+from openpype_modules.deadline.abstract_submit_deadline import requests_post
+
class CelactionSubmitDeadline(pyblish.api.InstancePlugin):
"""Submit CelAction2D scene to Deadline
@@ -30,11 +31,7 @@ class CelactionSubmitDeadline(pyblish.api.InstancePlugin):
context = instance.context
- # get default deadline webservice url from deadline module
- deadline_url = instance.context.data["defaultDeadline"]
- # if custom one is set in instance, use that
- if instance.data.get("deadlineUrl"):
- deadline_url = instance.data.get("deadlineUrl")
+ deadline_url = instance.data["deadline"]["url"]
assert deadline_url, "Requires Deadline Webservice URL"
self.deadline_url = "{}/api/jobs".format(deadline_url)
@@ -196,8 +193,11 @@ class CelactionSubmitDeadline(pyblish.api.InstancePlugin):
self.expected_files(instance, render_path)
self.log.debug("__ expectedFiles: `{}`".format(
instance.data["expectedFiles"]))
-
- response = requests.post(self.deadline_url, json=payload)
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
+ response = requests_post(self.deadline_url, json=payload,
+ auth=auth,
+ verify=verify)
if not response.ok:
self.log.error(
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py
index e3a4cd8030..9eea4d6549 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_fusion_deadline.py
@@ -2,17 +2,13 @@ import os
import json
import getpass
-import requests
-
import pyblish.api
+from openpype_modules.deadline.abstract_submit_deadline import requests_post
from ayon_core.pipeline.publish import (
AYONPyblishPluginMixin
)
-from ayon_core.lib import (
- BoolDef,
- NumberDef,
-)
+from ayon_core.lib import NumberDef
class FusionSubmitDeadline(
@@ -64,11 +60,6 @@ class FusionSubmitDeadline(
decimals=0,
minimum=1,
maximum=10
- ),
- BoolDef(
- "suspend_publish",
- default=False,
- label="Suspend publish"
)
]
@@ -80,10 +71,6 @@ class FusionSubmitDeadline(
attribute_values = self.get_attr_values_from_data(
instance.data)
- # add suspend_publish attributeValue to instance data
- instance.data["suspend_publish"] = attribute_values[
- "suspend_publish"]
-
context = instance.context
key = "__hasRun{}".format(self.__class__.__name__)
@@ -92,13 +79,9 @@ class FusionSubmitDeadline(
else:
context.data[key] = True
- from ayon_core.hosts.fusion.api.lib import get_frame_path
+ from ayon_fusion.api.lib import get_frame_path
- # get default deadline webservice url from deadline module
- deadline_url = instance.context.data["defaultDeadline"]
- # if custom one is set in instance, use that
- if instance.data.get("deadlineUrl"):
- deadline_url = instance.data.get("deadlineUrl")
+ deadline_url = instance.data["deadline"]["url"]
assert deadline_url, "Requires Deadline Webservice URL"
# Collect all saver instances in context that are to be rendered
@@ -258,7 +241,9 @@ class FusionSubmitDeadline(
# E.g. http://192.168.0.1:8082/api/jobs
url = "{}/api/jobs".format(deadline_url)
- response = requests.post(url, json=payload)
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
+ response = requests_post(url, json=payload, auth=auth, verify=verify)
if not response.ok:
raise Exception(response.text)
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py
index 6952604293..590abc3f12 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_houdini_render_deadline.py
@@ -10,7 +10,6 @@ from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
from ayon_core.lib import (
is_in_tests,
- BoolDef,
TextDef,
NumberDef
)
@@ -86,15 +85,10 @@ class HoudiniSubmitDeadline(
priority = 50
chunk_size = 1
group = ""
-
+
@classmethod
def get_attribute_defs(cls):
return [
- BoolDef(
- "suspend_publish",
- default=False,
- label="Suspend publish"
- ),
NumberDef(
"priority",
label="Priority",
@@ -194,7 +188,7 @@ class HoudiniSubmitDeadline(
job_info.Pool = instance.data.get("primaryPool")
job_info.SecondaryPool = instance.data.get("secondaryPool")
-
+
if split_render_job and is_export_job:
job_info.Priority = attribute_values.get(
"export_priority", self.export_priority
@@ -315,6 +309,11 @@ class HoudiniSubmitDeadline(
return attr.asdict(plugin_info)
def process(self, instance):
+ if not instance.data["farm"]:
+ self.log.debug("Render on farm is disabled. "
+ "Skipping deadline submission.")
+ return
+
super(HoudiniSubmitDeadline, self).process(instance)
# TODO: Avoid the need for this logic here, needed for submit publish
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py
index cba05f6948..4cb510f1cb 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_max_deadline.py
@@ -15,11 +15,11 @@ from ayon_core.pipeline.publish.lib import (
replace_with_published_scene_path
)
from ayon_core.pipeline.publish import KnownPublishError
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api.lib import (
get_current_renderer,
get_multipass_setting
)
-from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings
+from ayon_max.api.lib_rendersettings import RenderSettings
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
@@ -181,25 +181,35 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
self.log.debug("Submitting 3dsMax render..")
project_settings = instance.context.data["project_settings"]
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
if instance.data.get("multiCamera"):
self.log.debug("Submitting jobs for multiple cameras..")
payload = self._use_published_name_for_multiples(
payload_data, project_settings)
job_infos, plugin_infos = payload
for job_info, plugin_info in zip(job_infos, plugin_infos):
- self.submit(self.assemble_payload(job_info, plugin_info))
+ self.submit(
+ self.assemble_payload(job_info, plugin_info),
+ auth=auth,
+ verify=verify
+ )
else:
payload = self._use_published_name(payload_data, project_settings)
job_info, plugin_info = payload
- self.submit(self.assemble_payload(job_info, plugin_info))
+ self.submit(
+ self.assemble_payload(job_info, plugin_info),
+ auth=auth,
+ verify=verify
+ )
def _use_published_name(self, data, project_settings):
# Not all hosts can import these modules.
- from ayon_core.hosts.max.api.lib import (
+ from ayon_max.api.lib import (
get_current_renderer,
get_multipass_setting
)
- from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings
+ from ayon_max.api.lib_rendersettings import RenderSettings
instance = self._instance
job_info = copy.deepcopy(self.job_info)
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py
index 0300b12104..b1193fb914 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_maya_deadline.py
@@ -39,8 +39,8 @@ from ayon_core.lib import (
EnumDef,
is_in_tests,
)
-from ayon_core.hosts.maya.api.lib_rendersettings import RenderSettings
-from ayon_core.hosts.maya.api.lib import get_attr_in_layer
+from ayon_maya.api.lib_rendersettings import RenderSettings
+from ayon_maya.api.lib import get_attr_in_layer
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
@@ -292,7 +292,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
return plugin_payload
- def process_submission(self):
+ def process_submission(self, auth=None, verify=True):
from maya import cmds
instance = self._instance
@@ -332,7 +332,10 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
if "vrayscene" in instance.data["families"]:
self.log.debug("Submitting V-Ray scene render..")
vray_export_payload = self._get_vray_export_payload(payload_data)
- export_job = self.submit(vray_export_payload)
+
+ export_job = self.submit(vray_export_payload,
+ auth=auth,
+ verify=verify)
payload = self._get_vray_render_payload(payload_data)
@@ -351,7 +354,9 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
else:
# Submit main render job
job_info, plugin_info = payload
- self.submit(self.assemble_payload(job_info, plugin_info))
+ self.submit(self.assemble_payload(job_info, plugin_info),
+ auth=auth,
+ verify=verify)
def _tile_render(self, payload):
"""Submit as tile render per frame with dependent assembly jobs."""
@@ -451,7 +456,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
# Submit frame tile jobs
frame_tile_job_id = {}
for frame, tile_job_payload in frame_payloads.items():
- job_id = self.submit(tile_job_payload)
+ job_id = self.submit(tile_job_payload,
+ instance.data["deadline"]["auth"])
frame_tile_job_id[frame] = job_id
# Define assembly payloads
@@ -554,12 +560,18 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
# Submit assembly jobs
assembly_job_ids = []
num_assemblies = len(assembly_payloads)
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
for i, payload in enumerate(assembly_payloads):
self.log.debug(
"submitting assembly job {} of {}".format(i + 1,
num_assemblies)
)
- assembly_job_id = self.submit(payload)
+ assembly_job_id = self.submit(
+ payload,
+ auth=auth,
+ verify=verify
+ )
assembly_job_ids.append(assembly_job_id)
instance.data["assemblySubmissionJobs"] = assembly_job_ids
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py
index d70cb75bf3..db35c2ae67 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_nuke_deadline.py
@@ -4,9 +4,9 @@ import json
import getpass
from datetime import datetime
-import requests
import pyblish.api
+from openpype_modules.deadline.abstract_submit_deadline import requests_post
from ayon_core.pipeline.publish import (
AYONPyblishPluginMixin
)
@@ -76,11 +76,6 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
default=cls.use_gpu,
label="Use GPU"
),
- BoolDef(
- "suspend_publish",
- default=False,
- label="Suspend publish"
- ),
BoolDef(
"workfile_dependency",
default=cls.workfile_dependency,
@@ -100,20 +95,12 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
instance.data["attributeValues"] = self.get_attr_values_from_data(
instance.data)
- # add suspend_publish attributeValue to instance data
- instance.data["suspend_publish"] = instance.data["attributeValues"][
- "suspend_publish"]
-
families = instance.data["families"]
node = instance.data["transientData"]["node"]
context = instance.context
- # get default deadline webservice url from deadline module
- deadline_url = instance.context.data["defaultDeadline"]
- # if custom one is set in instance, use that
- if instance.data.get("deadlineUrl"):
- deadline_url = instance.data.get("deadlineUrl")
+ deadline_url = instance.data["deadline"]["url"]
assert deadline_url, "Requires Deadline Webservice URL"
self.deadline_url = "{}/api/jobs".format(deadline_url)
@@ -436,7 +423,13 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
self.log.debug("__ expectedFiles: `{}`".format(
instance.data["expectedFiles"]))
- response = requests.post(self.deadline_url, json=payload, timeout=10)
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
+ response = requests_post(self.deadline_url,
+ json=payload,
+ timeout=10,
+ auth=auth,
+ verify=verify)
if not response.ok:
raise Exception(response.text)
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py
index 4e4657d886..103f1355da 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_cache_job.py
@@ -5,10 +5,10 @@ import json
import re
from copy import deepcopy
-import requests
import ayon_api
import pyblish.api
+from openpype_modules.deadline.abstract_submit_deadline import requests_post
from ayon_core.pipeline import publish
from ayon_core.lib import EnumDef, is_in_tests
from ayon_core.pipeline.version_start import get_versioning_start
@@ -147,9 +147,6 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin,
instance_settings = self.get_attr_values_from_data(instance.data)
initial_status = instance_settings.get("publishJobState", "Active")
- # TODO: Remove this backwards compatibility of `suspend_publish`
- if instance.data.get("suspend_publish"):
- initial_status = "Suspended"
args = [
"--headless",
@@ -212,7 +209,10 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin,
self.log.debug("Submitting Deadline publish job ...")
url = "{}/api/jobs".format(self.deadline_url)
- response = requests.post(url, json=payload, timeout=10)
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
+ response = requests_post(
+ url, json=payload, timeout=10, auth=auth, verify=verify)
if not response.ok:
raise Exception(response.text)
@@ -344,11 +344,7 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin,
deadline_publish_job_id = None
if submission_type == "deadline":
- # get default deadline webservice url from deadline module
- self.deadline_url = instance.context.data["defaultDeadline"]
- # if custom one is set in instance, use that
- if instance.data.get("deadlineUrl"):
- self.deadline_url = instance.data.get("deadlineUrl")
+ self.deadline_url = instance.data["deadline"]["url"]
assert self.deadline_url, "Requires Deadline Webservice URL"
deadline_publish_job_id = \
@@ -356,7 +352,9 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin,
# Inject deadline url to instances.
for inst in instances:
- inst["deadlineUrl"] = self.deadline_url
+ if "deadline" not in inst:
+ inst["deadline"] = {}
+ inst["deadline"] = instance.data["deadline"]
# publish job file
publish_job = {
diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py
index 8def9cc63c..64313c5c4d 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py
@@ -5,11 +5,11 @@ import json
import re
from copy import deepcopy
-import requests
import clique
import ayon_api
import pyblish.api
+from openpype_modules.deadline.abstract_submit_deadline import requests_post
from ayon_core.pipeline import publish
from ayon_core.lib import EnumDef, is_in_tests
from ayon_core.pipeline.version_start import get_versioning_start
@@ -88,9 +88,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin,
hosts = ["fusion", "max", "maya", "nuke", "houdini",
"celaction", "aftereffects", "harmony", "blender"]
- families = ["render.farm", "render.frames_farm",
- "prerender.farm", "prerender.frames_farm",
- "renderlayer", "imagesequence",
+ families = ["render", "render.farm", "render.frames_farm",
+ "prerender", "prerender.farm", "prerender.frames_farm",
+ "renderlayer", "imagesequence", "image",
"vrayscene", "maxrender",
"arnold_rop", "mantra_rop",
"karma_rop", "vray_rop",
@@ -224,9 +224,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin,
instance_settings = self.get_attr_values_from_data(instance.data)
initial_status = instance_settings.get("publishJobState", "Active")
- # TODO: Remove this backwards compatibility of `suspend_publish`
- if instance.data.get("suspend_publish"):
- initial_status = "Suspended"
args = [
"--headless",
@@ -306,7 +303,10 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin,
self.log.debug("Submitting Deadline publish job ...")
url = "{}/api/jobs".format(self.deadline_url)
- response = requests.post(url, json=payload, timeout=10)
+ auth = instance.data["deadline"]["auth"]
+ verify = instance.data["deadline"]["verify"]
+ response = requests_post(
+ url, json=payload, timeout=10, auth=auth, verify=verify)
if not response.ok:
raise Exception(response.text)
@@ -314,7 +314,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin,
return deadline_publish_job_id
-
def process(self, instance):
# type: (pyblish.api.Instance) -> None
"""Process plugin.
@@ -461,18 +460,15 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin,
}
# get default deadline webservice url from deadline module
- self.deadline_url = instance.context.data["defaultDeadline"]
- # if custom one is set in instance, use that
- if instance.data.get("deadlineUrl"):
- self.deadline_url = instance.data.get("deadlineUrl")
+ self.deadline_url = instance.data["deadline"]["url"]
assert self.deadline_url, "Requires Deadline Webservice URL"
deadline_publish_job_id = \
self._submit_deadline_post_job(instance, render_job, instances)
- # Inject deadline url to instances.
+ # Inject deadline url to instances to query DL for job id for overrides
for inst in instances:
- inst["deadlineUrl"] = self.deadline_url
+ inst["deadline"] = instance.data["deadline"]
# publish job file
publish_job = {
diff --git a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_connection.py b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_connection.py
index a7b300beff..8fffd47786 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_connection.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_connection.py
@@ -1,5 +1,7 @@
import pyblish.api
+from ayon_core.pipeline import PublishXmlValidationError
+
from openpype_modules.deadline.abstract_submit_deadline import requests_get
@@ -8,27 +10,42 @@ class ValidateDeadlineConnection(pyblish.api.InstancePlugin):
label = "Validate Deadline Web Service"
order = pyblish.api.ValidatorOrder
- hosts = ["maya", "nuke"]
- families = ["renderlayer", "render"]
+ hosts = ["maya", "nuke", "aftereffects", "harmony", "fusion"]
+ families = ["renderlayer", "render", "render.farm"]
# cache
responses = {}
def process(self, instance):
- # get default deadline webservice url from deadline module
- deadline_url = instance.context.data["defaultDeadline"]
- # if custom one is set in instance, use that
- if instance.data.get("deadlineUrl"):
- deadline_url = instance.data.get("deadlineUrl")
- self.log.debug(
- "We have deadline URL on instance {}".format(deadline_url)
- )
+ if not instance.data.get("farm"):
+ self.log.debug("Should not be processed on farm, skipping.")
+ return
+
+ deadline_url = instance.data["deadline"]["url"]
assert deadline_url, "Requires Deadline Webservice URL"
+ kwargs = {}
+ if instance.data["deadline"]["require_authentication"]:
+ auth = instance.data["deadline"]["auth"]
+ kwargs["auth"] = auth
+
+ if not auth[0]:
+ raise PublishXmlValidationError(
+ self,
+ "Deadline requires authentication. "
+ "At least username is required to be set in "
+ "Site Settings.")
+
if deadline_url not in self.responses:
- self.responses[deadline_url] = requests_get(deadline_url)
+ self.responses[deadline_url] = requests_get(deadline_url, **kwargs)
response = self.responses[deadline_url]
+ if response.status_code == 401:
+ raise PublishXmlValidationError(
+ self,
+ "Deadline requires authentication. "
+ "Provided credentials are not working. "
+ "Please change them in Site Settings")
assert response.ok, "Response must be ok"
assert response.text.startswith("Deadline Web Service "), (
"Web service did not respond with 'Deadline Web Service'"
diff --git a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py
index 2feb044cf1..2fb511bf51 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py
@@ -37,8 +37,9 @@ class ValidateDeadlinePools(OptionalPyblishPluginMixin,
self.log.debug("Skipping local instance.")
return
- deadline_url = self.get_deadline_url(instance)
- pools = self.get_pools(deadline_url)
+ deadline_url = instance.data["deadline"]["url"]
+ pools = self.get_pools(deadline_url,
+ instance.data["deadline"].get("auth"))
invalid_pools = {}
primary_pool = instance.data.get("primaryPool")
@@ -61,22 +62,18 @@ class ValidateDeadlinePools(OptionalPyblishPluginMixin,
formatting_data={"pools_str": ", ".join(pools)}
)
- def get_deadline_url(self, instance):
- # get default deadline webservice url from deadline module
- deadline_url = instance.context.data["defaultDeadline"]
- if instance.data.get("deadlineUrl"):
- # if custom one is set in instance, use that
- deadline_url = instance.data.get("deadlineUrl")
- return deadline_url
-
- def get_pools(self, deadline_url):
+ def get_pools(self, deadline_url, auth):
if deadline_url not in self.pools_per_url:
self.log.debug(
"Querying available pools for Deadline url: {}".format(
deadline_url)
)
pools = DeadlineModule.get_deadline_pools(deadline_url,
+ auth=auth,
log=self.log)
+ # some DL return "none" as a pool name
+ if "none" not in pools:
+ pools.append("none")
self.log.info("Available pools: {}".format(pools))
self.pools_per_url[deadline_url] = pools
diff --git a/client/ayon_core/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py b/client/ayon_core/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py
index 6263526d5c..83e867408c 100644
--- a/client/ayon_core/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py
+++ b/client/ayon_core/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py
@@ -199,16 +199,16 @@ class ValidateExpectedFiles(pyblish.api.InstancePlugin):
(dict): Job info from Deadline
"""
- # get default deadline webservice url from deadline module
- deadline_url = instance.context.data["defaultDeadline"]
- # if custom one is set in instance, use that
- if instance.data.get("deadlineUrl"):
- deadline_url = instance.data.get("deadlineUrl")
+ deadline_url = instance.data["deadline"]["url"]
assert deadline_url, "Requires Deadline Webservice URL"
url = "{}/api/jobs?JobID={}".format(deadline_url, job_id)
try:
- response = requests_get(url)
+ kwargs = {}
+ auth = instance.data["deadline"]["auth"]
+ if auth:
+ kwargs["auth"] = auth
+ response = requests_get(url, **kwargs)
except requests.exceptions.ConnectionError:
self.log.error("Deadline is not accessible at "
"{}".format(deadline_url))
diff --git a/client/ayon_core/modules/deadline/version.py b/client/ayon_core/modules/deadline/version.py
new file mode 100644
index 0000000000..74acd0efba
--- /dev/null
+++ b/client/ayon_core/modules/deadline/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.12"
diff --git a/client/ayon_core/modules/job_queue/__init__.py b/client/ayon_core/modules/job_queue/__init__.py
index 0a4c62abfb..041782dd29 100644
--- a/client/ayon_core/modules/job_queue/__init__.py
+++ b/client/ayon_core/modules/job_queue/__init__.py
@@ -1,6 +1,9 @@
+from .version import __version__
from .addon import JobQueueAddon
__all__ = (
+ "__version__",
+
"JobQueueAddon",
)
diff --git a/client/ayon_core/modules/job_queue/addon.py b/client/ayon_core/modules/job_queue/addon.py
index 0fa54eb2f0..96f6ff0d4d 100644
--- a/client/ayon_core/modules/job_queue/addon.py
+++ b/client/ayon_core/modules/job_queue/addon.py
@@ -44,9 +44,12 @@ import platform
from ayon_core.addon import AYONAddon, click_wrap
from ayon_core.settings import get_studio_settings
+from .version import __version__
+
class JobQueueAddon(AYONAddon):
name = "job_queue"
+ version = __version__
def initialize(self, studio_settings):
addon_settings = studio_settings.get(self.name) or {}
diff --git a/client/ayon_core/modules/job_queue/version.py b/client/ayon_core/modules/job_queue/version.py
new file mode 100644
index 0000000000..5becc17c04
--- /dev/null
+++ b/client/ayon_core/modules/job_queue/version.py
@@ -0,0 +1 @@
+__version__ = "1.0.0"
diff --git a/client/ayon_core/modules/launcher_action.py b/client/ayon_core/modules/launcher_action.py
index 38e88d36ca..344b0bc389 100644
--- a/client/ayon_core/modules/launcher_action.py
+++ b/client/ayon_core/modules/launcher_action.py
@@ -7,6 +7,7 @@ from ayon_core.addon import AYONAddon, ITrayAction
class LauncherAction(AYONAddon, ITrayAction):
label = "Launcher"
name = "launcher_tool"
+ version = "1.0.0"
def initialize(self, settings):
diff --git a/client/ayon_core/modules/loader_action.py b/client/ayon_core/modules/loader_action.py
index 1e45db05dc..a58d7fd456 100644
--- a/client/ayon_core/modules/loader_action.py
+++ b/client/ayon_core/modules/loader_action.py
@@ -3,6 +3,7 @@ from ayon_core.addon import AYONAddon, ITrayAddon
class LoaderAddon(AYONAddon, ITrayAddon):
name = "loader_tool"
+ version = "1.0.0"
def initialize(self, settings):
# Tray attributes
diff --git a/client/ayon_core/modules/python_console_interpreter/addon.py b/client/ayon_core/modules/python_console_interpreter/addon.py
index ffad3ce707..b0dce2585e 100644
--- a/client/ayon_core/modules/python_console_interpreter/addon.py
+++ b/client/ayon_core/modules/python_console_interpreter/addon.py
@@ -4,6 +4,7 @@ from ayon_core.addon import AYONAddon, ITrayAction
class PythonInterpreterAction(AYONAddon, ITrayAction):
label = "Console"
name = "python_interpreter"
+ version = "1.0.0"
admin_action = True
def initialize(self, settings):
diff --git a/client/ayon_core/modules/royalrender/__init__.py b/client/ayon_core/modules/royalrender/__init__.py
index 121530beda..8bf207e7db 100644
--- a/client/ayon_core/modules/royalrender/__init__.py
+++ b/client/ayon_core/modules/royalrender/__init__.py
@@ -1,6 +1,9 @@
+from .version import __version__
from .addon import RoyalRenderAddon
__all__ = (
+ "__version__",
+
"RoyalRenderAddon",
)
diff --git a/client/ayon_core/modules/royalrender/addon.py b/client/ayon_core/modules/royalrender/addon.py
index e69cf9feec..264d3516c1 100644
--- a/client/ayon_core/modules/royalrender/addon.py
+++ b/client/ayon_core/modules/royalrender/addon.py
@@ -4,10 +4,13 @@ import os
from ayon_core.addon import AYONAddon, IPluginPaths
+from .version import __version__
+
class RoyalRenderAddon(AYONAddon, IPluginPaths):
"""Class providing basic Royal Render implementation logic."""
name = "royalrender"
+ version = __version__
# _rr_api = None
# @property
diff --git a/client/ayon_core/modules/royalrender/api.py b/client/ayon_core/modules/royalrender/api.py
index a69f88c43c..ef715811c5 100644
--- a/client/ayon_core/modules/royalrender/api.py
+++ b/client/ayon_core/modules/royalrender/api.py
@@ -7,7 +7,7 @@ from ayon_core.lib import Logger, run_subprocess, AYONSettingsRegistry
from ayon_core.lib.vendor_bin_utils import find_tool_in_custom_paths
from .rr_job import SubmitFile
-from .rr_job import RRjob, SubmitterParameter # noqa F401
+from .rr_job import RRJob, SubmitterParameter # noqa F401
class Api:
diff --git a/client/ayon_core/modules/royalrender/version.py b/client/ayon_core/modules/royalrender/version.py
new file mode 100644
index 0000000000..485f44ac21
--- /dev/null
+++ b/client/ayon_core/modules/royalrender/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.1"
diff --git a/client/ayon_core/modules/timers_manager/__init__.py b/client/ayon_core/modules/timers_manager/__init__.py
index 5d7a4166d3..1ec0d9b74b 100644
--- a/client/ayon_core/modules/timers_manager/__init__.py
+++ b/client/ayon_core/modules/timers_manager/__init__.py
@@ -1,7 +1,10 @@
+from .version import __version__
from .timers_manager import (
TimersManager
)
__all__ = (
+ "__version__",
+
"TimersManager",
)
diff --git a/client/ayon_core/modules/timers_manager/timers_manager.py b/client/ayon_core/modules/timers_manager/timers_manager.py
index 4212ff6b25..2aac7b2a49 100644
--- a/client/ayon_core/modules/timers_manager/timers_manager.py
+++ b/client/ayon_core/modules/timers_manager/timers_manager.py
@@ -10,6 +10,7 @@ from ayon_core.addon import (
)
from ayon_core.lib.events import register_event_callback
+from .version import __version__
from .exceptions import InvalidContextError
TIMER_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -96,6 +97,7 @@ class TimersManager(
See `ExampleTimersManagerConnector`.
"""
name = "timers_manager"
+ version = __version__
label = "Timers Service"
_required_methods = (
diff --git a/client/ayon_core/modules/timers_manager/version.py b/client/ayon_core/modules/timers_manager/version.py
new file mode 100644
index 0000000000..485f44ac21
--- /dev/null
+++ b/client/ayon_core/modules/timers_manager/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.1"
diff --git a/client/ayon_core/modules/webserver/__init__.py b/client/ayon_core/modules/webserver/__init__.py
index 0d3f767638..a978b94bc4 100644
--- a/client/ayon_core/modules/webserver/__init__.py
+++ b/client/ayon_core/modules/webserver/__init__.py
@@ -1,8 +1,11 @@
+from .version import __version__
from .webserver_module import (
WebServerAddon
)
__all__ = (
+ "__version__",
+
"WebServerAddon",
)
diff --git a/client/ayon_core/modules/webserver/version.py b/client/ayon_core/modules/webserver/version.py
new file mode 100644
index 0000000000..5becc17c04
--- /dev/null
+++ b/client/ayon_core/modules/webserver/version.py
@@ -0,0 +1 @@
+__version__ = "1.0.0"
diff --git a/client/ayon_core/modules/webserver/webserver_module.py b/client/ayon_core/modules/webserver/webserver_module.py
index c324e0dd18..997b6f754c 100644
--- a/client/ayon_core/modules/webserver/webserver_module.py
+++ b/client/ayon_core/modules/webserver/webserver_module.py
@@ -26,9 +26,12 @@ import socket
from ayon_core import resources
from ayon_core.addon import AYONAddon, ITrayService
+from .version import __version__
+
class WebServerAddon(AYONAddon, ITrayService):
name = "webserver"
+ version = __version__
label = "WebServer"
webserver_url_env = "AYON_WEBSERVER_URL"
diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py
index efa3bbf968..099616ff4a 100644
--- a/client/ayon_core/pipeline/colorspace.py
+++ b/client/ayon_core/pipeline/colorspace.py
@@ -8,16 +8,20 @@ import tempfile
import warnings
from copy import deepcopy
+import ayon_api
+
from ayon_core import AYON_CORE_ROOT
from ayon_core.settings import get_project_settings
from ayon_core.lib import (
+ filter_profiles,
StringTemplate,
run_ayon_launcher_process,
- Logger
+ Logger,
)
-from ayon_core.pipeline import Anatomy
from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS
-
+from ayon_core.pipeline import Anatomy
+from ayon_core.pipeline.template_data import get_template_data
+from ayon_core.pipeline.load import get_representation_path_with_anatomy
log = Logger.get_logger(__name__)
@@ -32,10 +36,6 @@ class CachedData:
}
-class DeprecatedWarning(DeprecationWarning):
- pass
-
-
def deprecated(new_destination):
"""Mark functions as deprecated.
@@ -60,13 +60,13 @@ def deprecated(new_destination):
@functools.wraps(decorated_func)
def wrapper(*args, **kwargs):
- warnings.simplefilter("always", DeprecatedWarning)
+ warnings.simplefilter("always", DeprecationWarning)
warnings.warn(
(
"Call to deprecated function '{}'"
"\nFunction was moved or removed.{}"
).format(decorated_func.__name__, warning_message),
- category=DeprecatedWarning,
+ category=DeprecationWarning,
stacklevel=4
)
return decorated_func(*args, **kwargs)
@@ -81,28 +81,54 @@ def deprecated(new_destination):
def _make_temp_json_file():
"""Wrapping function for json temp file
"""
+ temporary_json_file = None
try:
# Store dumped json to temporary file
- temporary_json_file = tempfile.NamedTemporaryFile(
+ with tempfile.NamedTemporaryFile(
mode="w", suffix=".json", delete=False
- )
- temporary_json_file.close()
- temporary_json_filepath = temporary_json_file.name.replace(
- "\\", "/"
- )
+ ) as tmpfile:
+ temporary_json_filepath = tmpfile.name.replace("\\", "/")
yield temporary_json_filepath
- except IOError as _error:
+ except IOError as exc:
raise IOError(
- "Unable to create temp json file: {}".format(
- _error
- )
+ "Unable to create temp json file: {}".format(exc)
)
finally:
# Remove the temporary json
- os.remove(temporary_json_filepath)
+ if temporary_json_file is not None:
+ os.remove(temporary_json_filepath)
+
+
+def has_compatible_ocio_package():
+ """Current process has available compatible 'PyOpenColorIO'.
+
+ Returns:
+ bool: True if compatible package is available.
+
+ """
+ if CachedData.has_compatible_ocio_package is not None:
+ return CachedData.has_compatible_ocio_package
+
+ is_compatible = False
+ try:
+ import PyOpenColorIO
+
+ # Check if PyOpenColorIO is compatible
+ # - version 2.0.0 or higher is required
+ # NOTE version 1 does not have '__version__' attribute
+ if hasattr(PyOpenColorIO, "__version__"):
+ version_parts = PyOpenColorIO.__version__.split(".")
+ major = int(version_parts[0])
+ is_compatible = (major, ) >= (2, )
+ except ImportError:
+ pass
+
+ CachedData.has_compatible_ocio_package = is_compatible
+ # compatible
+ return CachedData.has_compatible_ocio_package
def get_ocio_config_script_path():
@@ -110,53 +136,58 @@ def get_ocio_config_script_path():
Returns:
str: path string
+
"""
- return os.path.normpath(
- os.path.join(
- AYON_CORE_ROOT,
- "scripts",
- "ocio_wrapper.py"
- )
+ return os.path.join(
+ os.path.normpath(AYON_CORE_ROOT),
+ "scripts",
+ "ocio_wrapper.py"
)
def get_colorspace_name_from_filepath(
- filepath, host_name, project_name,
- config_data=None, file_rules=None,
+ filepath,
+ host_name,
+ project_name,
+ config_data,
+ file_rules=None,
project_settings=None,
validate=True
):
"""Get colorspace name from filepath
Args:
- filepath (str): path string, file rule pattern is tested on it
- host_name (str): host name
- project_name (str): project name
- config_data (Optional[dict]): config path and template in dict.
- Defaults to None.
- file_rules (Optional[dict]): file rule data from settings.
- Defaults to None.
- project_settings (Optional[dict]): project settings. Defaults to None.
+ filepath (str): Path string, file rule pattern is tested on it.
+ host_name (str): Host name.
+ project_name (str): Project name.
+ config_data (dict): Config path and template in dict.
+ file_rules (Optional[dict]): File rule data from settings.
+ project_settings (Optional[dict]): Project settings.
validate (Optional[bool]): should resulting colorspace be validated
- with config file? Defaults to True.
+ with config file? Defaults to True.
Returns:
- str: name of colorspace
- """
- project_settings, config_data, file_rules = _get_context_settings(
- host_name, project_name,
- config_data=config_data, file_rules=file_rules,
- project_settings=project_settings
- )
+ Union[str, None]: name of colorspace
+ """
if not config_data:
# in case global or host color management is not enabled
return None
+ if file_rules is None:
+ if project_settings is None:
+ project_settings = get_project_settings(project_name)
+ file_rules = get_imageio_file_rules(
+ project_name, host_name, project_settings
+ )
+
# use ImageIO file rules
colorspace_name = get_imageio_file_rules_colorspace_from_filepath(
- filepath, host_name, project_name,
- config_data=config_data, file_rules=file_rules,
+ filepath,
+ host_name,
+ project_name,
+ config_data=config_data,
+ file_rules=file_rules,
project_settings=project_settings
)
@@ -182,47 +213,18 @@ def get_colorspace_name_from_filepath(
# validate matching colorspace with config
if validate:
validate_imageio_colorspace_in_config(
- config_data["path"], colorspace_name)
+ config_data["path"], colorspace_name
+ )
return colorspace_name
-# TODO: remove this in future - backward compatibility
-@deprecated("get_imageio_file_rules_colorspace_from_filepath")
-def get_imageio_colorspace_from_filepath(*args, **kwargs):
- return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs)
-
-# TODO: remove this in future - backward compatibility
-@deprecated("get_imageio_file_rules_colorspace_from_filepath")
-def get_colorspace_from_filepath(*args, **kwargs):
- return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs)
-
-
-def _get_context_settings(
- host_name, project_name,
- config_data=None, file_rules=None,
- project_settings=None
-):
- project_settings = project_settings or get_project_settings(
- project_name
- )
-
- config_data = config_data or get_imageio_config(
- project_name, host_name, project_settings)
-
- # in case host color management is not enabled
- if not config_data:
- return (None, None, None)
-
- file_rules = file_rules or get_imageio_file_rules(
- project_name, host_name, project_settings)
-
- return project_settings, config_data, file_rules
-
-
def get_imageio_file_rules_colorspace_from_filepath(
- filepath, host_name, project_name,
- config_data=None, file_rules=None,
+ filepath,
+ host_name,
+ project_name,
+ config_data,
+ file_rules=None,
project_settings=None
):
"""Get colorspace name from filepath
@@ -230,28 +232,28 @@ def get_imageio_file_rules_colorspace_from_filepath(
ImageIO Settings file rules are tested for matching rule.
Args:
- filepath (str): path string, file rule pattern is tested on it
- host_name (str): host name
- project_name (str): project name
- config_data (Optional[dict]): config path and template in dict.
- Defaults to None.
- file_rules (Optional[dict]): file rule data from settings.
- Defaults to None.
- project_settings (Optional[dict]): project settings. Defaults to None.
+ filepath (str): Path string, file rule pattern is tested on it.
+ host_name (str): Host name.
+ project_name (str): Project name.
+ config_data (dict): Config path and template in dict.
+ file_rules (Optional[dict]): File rule data from settings.
+ project_settings (Optional[dict]): Project settings.
Returns:
- str: name of colorspace
- """
- project_settings, config_data, file_rules = _get_context_settings(
- host_name, project_name,
- config_data=config_data, file_rules=file_rules,
- project_settings=project_settings
- )
+ Union[str, None]: Name of colorspace.
+ """
if not config_data:
# in case global or host color management is not enabled
return None
+ if file_rules is None:
+ if project_settings is None:
+ project_settings = get_project_settings(project_name)
+ file_rules = get_imageio_file_rules(
+ project_name, host_name, project_settings
+ )
+
# match file rule from path
colorspace_name = None
for file_rule in file_rules:
@@ -282,26 +284,48 @@ def get_config_file_rules_colorspace_from_filepath(config_path, filepath):
Returns:
Union[str, None]: matching colorspace name
+
"""
- if not compatibility_check():
- # python environment is not compatible with PyOpenColorIO
- # needs to be run in subprocess
+ if has_compatible_ocio_package():
+ result_data = _get_config_file_rules_colorspace_from_filepath(
+ config_path, filepath
+ )
+ else:
result_data = _get_wrapped_with_subprocess(
- "colorspace", "get_config_file_rules_colorspace_from_filepath",
+ "get_config_file_rules_colorspace_from_filepath",
config_path=config_path,
filepath=filepath
)
- if result_data:
- return result_data[0]
-
- # TODO: refactor this so it is not imported but part of this file
- from ayon_core.scripts.ocio_wrapper import _get_config_file_rules_colorspace_from_filepath # noqa: E501
-
- result_data = _get_config_file_rules_colorspace_from_filepath(
- config_path, filepath)
if result_data:
return result_data[0]
+ return None
+
+
+def get_config_version_data(config_path):
+ """Return major and minor version info.
+
+ Args:
+ config_path (str): path string leading to config.ocio
+
+ Raises:
+ IOError: Input config does not exist.
+
+ Returns:
+ dict: minor and major keys with values
+
+ """
+ if config_path not in CachedData.config_version_data:
+ if has_compatible_ocio_package():
+ version_data = _get_config_version_data(config_path)
+ else:
+ version_data = _get_wrapped_with_subprocess(
+ "get_config_version_data",
+ config_path=config_path
+ )
+ CachedData.config_version_data[config_path] = version_data
+
+ return deepcopy(CachedData.config_version_data[config_path])
def parse_colorspace_from_filepath(
@@ -344,10 +368,10 @@ def parse_colorspace_from_filepath(
pattern = "|".join(
# Allow to match spaces also as underscores because the
# integrator replaces spaces with underscores in filenames
- re.escape(colorspace) for colorspace in
+ re.escape(colorspace)
# Sort by longest first so the regex matches longer matches
# over smaller matches, e.g. matching 'Output - sRGB' over 'sRGB'
- sorted(colorspaces, key=len, reverse=True)
+ for colorspace in sorted(colorspaces, key=len, reverse=True)
)
return re.compile(pattern)
@@ -395,6 +419,7 @@ def validate_imageio_colorspace_in_config(config_path, colorspace_name):
Returns:
bool: True if exists
+
"""
colorspaces = get_ocio_config_colorspaces(config_path)["colorspaces"]
if colorspace_name not in colorspaces:
@@ -405,28 +430,10 @@ def validate_imageio_colorspace_in_config(config_path, colorspace_name):
return True
-# TODO: remove this in future - backward compatibility
-@deprecated("_get_wrapped_with_subprocess")
-def get_data_subprocess(config_path, data_type):
- """[Deprecated] Get data via subprocess
-
- Wrapper for Python 2 hosts.
+def _get_wrapped_with_subprocess(command, **kwargs):
+ """Get data via subprocess.
Args:
- config_path (str): path leading to config.ocio file
- """
- return _get_wrapped_with_subprocess(
- "config", data_type, in_path=config_path,
- )
-
-
-def _get_wrapped_with_subprocess(command_group, command, **kwargs):
- """Get data via subprocess
-
- Wrapper for Python 2 hosts.
-
- Args:
- command_group (str): command group name
command (str): command name
**kwargs: command arguments
@@ -436,14 +443,15 @@ def _get_wrapped_with_subprocess(command_group, command, **kwargs):
with _make_temp_json_file() as tmp_json_path:
# Prepare subprocess arguments
args = [
- "run", get_ocio_config_script_path(),
- command_group, command
+ "run",
+ get_ocio_config_script_path(),
+ command
]
- for key_, value_ in kwargs.items():
- args.extend(("--{}".format(key_), value_))
+ for key, value in kwargs.items():
+ args.extend(("--{}".format(key), value))
- args.append("--out_path")
+ args.append("--output_path")
args.append(tmp_json_path)
log.info("Executing: {}".format(" ".join(args)))
@@ -451,55 +459,23 @@ def _get_wrapped_with_subprocess(command_group, command, **kwargs):
run_ayon_launcher_process(*args, logger=log)
# return all colorspaces
- with open(tmp_json_path, "r") as f_:
- return json.load(f_)
+ with open(tmp_json_path, "r") as stream:
+ return json.load(stream)
-# TODO: this should be part of ocio_wrapper.py
-def compatibility_check():
- """Making sure PyOpenColorIO is importable"""
- if CachedData.has_compatible_ocio_package is not None:
- return CachedData.has_compatible_ocio_package
-
- try:
- import PyOpenColorIO # noqa: F401
- CachedData.has_compatible_ocio_package = True
- except ImportError:
- CachedData.has_compatible_ocio_package = False
-
- # compatible
- return CachedData.has_compatible_ocio_package
-
-
-# TODO: this should be part of ocio_wrapper.py
def compatibility_check_config_version(config_path, major=1, minor=None):
"""Making sure PyOpenColorIO config version is compatible"""
- if not CachedData.config_version_data.get(config_path):
- if compatibility_check():
- # TODO: refactor this so it is not imported but part of this file
- from ayon_core.scripts.ocio_wrapper import _get_version_data
-
- CachedData.config_version_data[config_path] = \
- _get_version_data(config_path)
-
- else:
- # python environment is not compatible with PyOpenColorIO
- # needs to be run in subprocess
- CachedData.config_version_data[config_path] = \
- _get_wrapped_with_subprocess(
- "config", "get_version", config_path=config_path
- )
+ version_data = get_config_version_data(config_path)
# check major version
- if CachedData.config_version_data[config_path]["major"] != major:
+ if version_data["major"] != major:
return False
# check minor version
- if minor and CachedData.config_version_data[config_path]["minor"] != minor:
+ if minor is not None and version_data["minor"] != minor:
return False
- # compatible
return True
@@ -514,23 +490,19 @@ def get_ocio_config_colorspaces(config_path):
Returns:
dict: colorspace and family in couple
+
"""
- if not CachedData.ocio_config_colorspaces.get(config_path):
- if not compatibility_check():
- # python environment is not compatible with PyOpenColorIO
- # needs to be run in subprocess
- CachedData.ocio_config_colorspaces[config_path] = \
- _get_wrapped_with_subprocess(
- "config", "get_colorspace", in_path=config_path
- )
+ if config_path not in CachedData.ocio_config_colorspaces:
+ if has_compatible_ocio_package():
+ config_colorspaces = _get_ocio_config_colorspaces(config_path)
else:
- # TODO: refactor this so it is not imported but part of this file
- from ayon_core.scripts.ocio_wrapper import _get_colorspace_data
+ config_colorspaces = _get_wrapped_with_subprocess(
+ "get_ocio_config_colorspaces",
+ config_path=config_path
+ )
+ CachedData.ocio_config_colorspaces[config_path] = config_colorspaces
- CachedData.ocio_config_colorspaces[config_path] = \
- _get_colorspace_data(config_path)
-
- return CachedData.ocio_config_colorspaces[config_path]
+ return deepcopy(CachedData.ocio_config_colorspaces[config_path])
def convert_colorspace_enumerator_item(
@@ -540,11 +512,12 @@ def convert_colorspace_enumerator_item(
"""Convert colorspace enumerator item to dictionary
Args:
- colorspace_item (str): colorspace and family in couple
- config_items (dict[str,dict]): colorspace data
+ colorspace_enum_item (str): Colorspace and family in couple.
+ config_items (dict[str,dict]): Colorspace data.
Returns:
dict: colorspace data
+
"""
if "::" not in colorspace_enum_item:
return None
@@ -603,16 +576,18 @@ def get_colorspaces_enumerator_items(
Families can be used for building menu and submenus in gui.
Args:
- config_items (dict[str,dict]): colorspace data coming from
- `get_ocio_config_colorspaces` function
- include_aliases (bool): include aliases in result
- include_looks (bool): include looks in result
- include_roles (bool): include roles in result
+ config_items (dict[str,dict]): Colorspace data coming from
+ `get_ocio_config_colorspaces` function.
+ include_aliases (Optional[bool]): Include aliases in result.
+ include_looks (Optional[bool]): Include looks in result.
+ include_roles (Optional[bool]): Include roles in result.
+ include_display_views (Optional[bool]): Include display views
+ in result.
Returns:
- list[tuple[str,str]]: colorspace and family in couple
+ list[tuple[str, str]]: Colorspace and family in couples.
+
"""
- labeled_colorspaces = []
aliases = set()
colorspaces = set()
looks = set()
@@ -622,86 +597,86 @@ def get_colorspaces_enumerator_items(
if items_type == "colorspaces":
for color_name, color_data in colorspace_items.items():
if color_data.get("aliases"):
- aliases.update([
+ aliases.update({
(
"aliases::{}".format(alias_name),
"[alias] {} ({})".format(alias_name, color_name)
)
for alias_name in color_data["aliases"]
- ])
+ })
colorspaces.add((
"{}::{}".format(items_type, color_name),
"[colorspace] {}".format(color_name)
))
elif items_type == "looks":
- looks.update([
+ looks.update({
(
"{}::{}".format(items_type, name),
"[look] {} ({})".format(name, role_data["process_space"])
)
for name, role_data in colorspace_items.items()
- ])
+ })
elif items_type == "displays_views":
- display_views.update([
+ display_views.update({
(
"{}::{}".format(items_type, name),
"[view (display)] {}".format(name)
)
for name, _ in colorspace_items.items()
- ])
+ })
elif items_type == "roles":
- roles.update([
+ roles.update({
(
"{}::{}".format(items_type, name),
"[role] {} ({})".format(name, role_data["colorspace"])
)
for name, role_data in colorspace_items.items()
- ])
+ })
- if roles and include_roles:
- roles = sorted(roles, key=lambda x: x[0])
- labeled_colorspaces.extend(roles)
+ def _sort_key_getter(item):
+ """Use colorspace for sorting.
- # add colorspaces as second so it is not first in menu
- colorspaces = sorted(colorspaces, key=lambda x: x[0])
- labeled_colorspaces.extend(colorspaces)
+ Args:
+ item (tuple[str, str]): Item with colorspace and label.
- if aliases and include_aliases:
- aliases = sorted(aliases, key=lambda x: x[0])
- labeled_colorspaces.extend(aliases)
+ Returns:
+ str: Colorspace.
- if looks and include_looks:
- looks = sorted(looks, key=lambda x: x[0])
- labeled_colorspaces.extend(looks)
+ """
+ return item[0]
- if display_views and include_display_views:
- display_views = sorted(display_views, key=lambda x: x[0])
- labeled_colorspaces.extend(display_views)
+ labeled_colorspaces = []
+ if include_roles:
+ labeled_colorspaces.extend(
+ sorted(roles, key=_sort_key_getter)
+ )
+
+ # Add colorspaces after roles, so it is not first in menu
+ labeled_colorspaces.extend(
+ sorted(colorspaces, key=_sort_key_getter)
+ )
+
+ if include_aliases:
+ labeled_colorspaces.extend(
+ sorted(aliases, key=_sort_key_getter)
+ )
+
+ if include_looks:
+ labeled_colorspaces.extend(
+ sorted(looks, key=_sort_key_getter)
+ )
+
+ if include_display_views:
+ labeled_colorspaces.extend(
+ sorted(display_views, key=_sort_key_getter)
+ )
return labeled_colorspaces
-# TODO: remove this in future - backward compatibility
-@deprecated("_get_wrapped_with_subprocess")
-def get_colorspace_data_subprocess(config_path):
- """[Deprecated] Get colorspace data via subprocess
-
- Wrapper for Python 2 hosts.
-
- Args:
- config_path (str): path leading to config.ocio file
-
- Returns:
- dict: colorspace and family in couple
- """
- return _get_wrapped_with_subprocess(
- "config", "get_colorspace", in_path=config_path
- )
-
-
def get_ocio_config_views(config_path):
"""Get all viewer data
@@ -713,212 +688,346 @@ def get_ocio_config_views(config_path):
Returns:
dict: `display/viewer` and viewer data
+
"""
- if not compatibility_check():
- # python environment is not compatible with PyOpenColorIO
- # needs to be run in subprocess
- return _get_wrapped_with_subprocess(
- "config", "get_views", in_path=config_path
- )
+ if has_compatible_ocio_package():
+ return _get_ocio_config_views(config_path)
- # TODO: refactor this so it is not imported but part of this file
- from ayon_core.scripts.ocio_wrapper import _get_views_data
-
- return _get_views_data(config_path)
-
-
-# TODO: remove this in future - backward compatibility
-@deprecated("_get_wrapped_with_subprocess")
-def get_views_data_subprocess(config_path):
- """[Deprecated] Get viewers data via subprocess
-
- Wrapper for Python 2 hosts.
-
- Args:
- config_path (str): path leading to config.ocio file
-
- Returns:
- dict: `display/viewer` and viewer data
- """
return _get_wrapped_with_subprocess(
- "config", "get_views", in_path=config_path
+ "get_ocio_config_views",
+ config_path=config_path
)
-def get_imageio_config(
+def _get_global_config_data(
project_name,
host_name,
- project_settings=None,
- anatomy_data=None,
+ anatomy,
+ template_data,
+ imageio_global,
+ folder_id,
+ log,
+):
+ """Get global config data.
+
+ Global config from core settings is using profiles that are based on
+ host name, task name and task type. The filtered profile can define 3
+ types of config sources:
+ 1. AYON ocio addon configs.
+ 2. Custom path to ocio config.
+ 3. Path to 'ocioconfig' representation on product. Name of product can be
+ defined in settings. Product name can be regex but exact match is
+ always preferred.
+
+ None is returned when no profile is found, when path
+
+ Args:
+ project_name (str): Project name.
+ host_name (str): Host name.
+ anatomy (Anatomy): Project anatomy object.
+ template_data (dict[str, Any]): Template data.
+ imageio_global (dict[str, Any]): Core imagio settings.
+ folder_id (Union[dict[str, Any], None]): Folder id.
+ log (logging.Logger): Logger object.
+
+ Returns:
+ Union[dict[str, str], None]: Config data with path and template
+ or None.
+
+ """
+ task_name = task_type = None
+ task_data = template_data.get("task")
+ if task_data:
+ task_name = task_data["name"]
+ task_type = task_data["type"]
+
+ filter_values = {
+ "task_names": task_name,
+ "task_types": task_type,
+ "host_names": host_name,
+ }
+ profile = filter_profiles(
+ imageio_global["ocio_config_profiles"], filter_values
+ )
+ if profile is None:
+ log.info(f"No config profile matched filters {str(filter_values)}")
+ return None
+
+ profile_type = profile["type"]
+ if profile_type in ("builtin_path", "custom_path"):
+ template = profile[profile_type]
+ result = StringTemplate.format_strict_template(
+ template, template_data
+ )
+ normalized_path = str(result.normalized())
+ if not os.path.exists(normalized_path):
+ log.warning(f"Path was not found '{normalized_path}'.")
+ return None
+
+ return {
+ "path": normalized_path,
+ "template": template
+ }
+
+ # TODO decide if this is the right name for representation
+ repre_name = "ocioconfig"
+
+ folder_info = template_data.get("folder")
+ if not folder_info:
+ log.warning("Folder info is missing.")
+ return None
+ folder_path = folder_info["path"]
+
+ product_name = profile["product_name"]
+ if folder_id is None:
+ folder_entity = ayon_api.get_folder_by_path(
+ project_name, folder_path, fields={"id"}
+ )
+ if not folder_entity:
+ log.warning(f"Folder entity '{folder_path}' was not found..")
+ return None
+ folder_id = folder_entity["id"]
+
+ product_entities_by_name = {
+ product_entity["name"]: product_entity
+ for product_entity in ayon_api.get_products(
+ project_name,
+ folder_ids={folder_id},
+ product_name_regex=product_name,
+ fields={"id", "name"}
+ )
+ }
+ if not product_entities_by_name:
+ log.debug(
+ f"No product entities were found for folder '{folder_path}' with"
+ f" product name filter '{product_name}'."
+ )
+ return None
+
+ # Try to use exact match first, otherwise use first available product
+ product_entity = product_entities_by_name.get(product_name)
+ if product_entity is None:
+ product_entity = next(iter(product_entities_by_name.values()))
+
+ product_name = product_entity["name"]
+ # Find last product version
+ version_entity = ayon_api.get_last_version_by_product_id(
+ project_name,
+ product_id=product_entity["id"],
+ fields={"id"}
+ )
+ if not version_entity:
+ log.info(
+ f"Product '{product_name}' does not have available any versions."
+ )
+ return None
+
+ # Find 'ocioconfig' representation entity
+ repre_entity = ayon_api.get_representation_by_name(
+ project_name,
+ representation_name=repre_name,
+ version_id=version_entity["id"],
+ )
+ if not repre_entity:
+ log.debug(
+ f"Representation '{repre_name}'"
+ f" not found on product '{product_name}'."
+ )
+ return None
+
+ path = get_representation_path_with_anatomy(repre_entity, anatomy)
+ template = repre_entity["attrib"]["template"]
+ return {
+ "path": path,
+ "template": template,
+ }
+
+
+def get_imageio_config_preset(
+ project_name,
+ folder_path,
+ task_name,
+ host_name,
anatomy=None,
- env=None
+ project_settings=None,
+ template_data=None,
+ env=None,
+ folder_id=None,
):
"""Returns config data from settings
- Config path is formatted in `path` key
- and original settings input is saved into `template` key.
+ Output contains 'path' key and 'template' key holds its template.
+
+ Template data can be prepared with 'get_template_data'.
Args:
- project_name (str): project name
- host_name (str): host name
+ project_name (str): Project name.
+ folder_path (str): Folder path.
+ task_name (str): Task name.
+ host_name (str): Host name.
+ anatomy (Optional[Anatomy]): Project anatomy object.
project_settings (Optional[dict]): Project settings.
- anatomy_data (Optional[dict]): anatomy formatting data.
- anatomy (Optional[Anatomy]): Anatomy object.
- env (Optional[dict]): Environment variables.
+ template_data (Optional[dict]): Template data used for
+ template formatting.
+ env (Optional[dict]): Environment variables. Environments are used
+ for template formatting too. Values from 'os.environ' are used
+ when not provided.
+ folder_id (Optional[str]): Folder id. Is used only when config path
+ is received from published representation. Is autofilled when
+ not provided.
Returns:
dict: config path data or empty dict
+
"""
- project_settings = project_settings or get_project_settings(project_name)
- anatomy = anatomy or Anatomy(project_name)
-
- if not anatomy_data:
- from ayon_core.pipeline.context_tools import (
- get_current_context_template_data)
- anatomy_data = get_current_context_template_data()
-
- formatting_data = deepcopy(anatomy_data)
-
- # Add project roots to anatomy data
- formatting_data["root"] = anatomy.roots
- formatting_data["platform"] = platform.system().lower()
+ if not project_settings:
+ project_settings = get_project_settings(project_name)
# Get colorspace settings
imageio_global, imageio_host = _get_imageio_settings(
- project_settings, host_name)
+ project_settings, host_name
+ )
+ # Global color management must be enabled to be able to use host settings
+ if not imageio_global["activate_global_color_management"]:
+ log.info("Colorspace management is disabled globally.")
+ return {}
# Host 'ocio_config' is optional
host_ocio_config = imageio_host.get("ocio_config") or {}
-
- # Global color management must be enabled to be able to use host settings
- activate_color_management = imageio_global.get(
- "activate_global_color_management")
- # TODO: remove this in future - backward compatibility
- # For already saved overrides from previous version look for 'enabled'
- # on host settings.
- if activate_color_management is None:
- activate_color_management = host_ocio_config.get("enabled", False)
-
- if not activate_color_management:
- # if global settings are disabled return empty dict because
- # it is expected that no colorspace management is needed
- log.info("Colorspace management is disabled globally.")
- return {}
+ # TODO remove
+ # - backward compatibility when host settings had only 'enabled' flag
+ # the flag was split into 'activate_global_color_management'
+ # and 'override_global_config'
+ host_ocio_config_enabled = host_ocio_config.get("enabled", False)
# Check if host settings group is having 'activate_host_color_management'
# - if it does not have activation key then default it to True so it uses
# global settings
- # This is for backward compatibility.
- # TODO: in future rewrite this to be more explicit
activate_host_color_management = imageio_host.get(
- "activate_host_color_management")
-
- # TODO: remove this in future - backward compatibility
+ "activate_host_color_management"
+ )
if activate_host_color_management is None:
- activate_host_color_management = host_ocio_config.get("enabled", False)
+ activate_host_color_management = host_ocio_config_enabled
if not activate_host_color_management:
# if host settings are disabled return False because
# it is expected that no colorspace management is needed
log.info(
- "Colorspace management for host '{}' is disabled.".format(
- host_name)
+ f"Colorspace management for host '{host_name}' is disabled."
)
return {}
- # get config path from either global or host settings
- # depending on override flag
+ project_entity = None
+ if anatomy is None:
+ project_entity = ayon_api.get_project(project_name)
+ anatomy = Anatomy(project_name, project_entity=project_entity)
+
+ if env is None:
+ env = dict(os.environ.items())
+
+ if template_data:
+ template_data = deepcopy(template_data)
+ else:
+ if not project_entity:
+ project_entity = ayon_api.get_project(project_name)
+
+ folder_entity = task_entity = folder_id = None
+ if folder_path:
+ folder_entity = ayon_api.get_folder_by_path(
+ project_name, folder_path
+ )
+ folder_id = folder_entity["id"]
+
+ if folder_id and task_name:
+ task_entity = ayon_api.get_task_by_name(
+ project_name, folder_id, task_name
+ )
+ template_data = get_template_data(
+ project_entity,
+ folder_entity,
+ task_entity,
+ host_name,
+ project_settings,
+ )
+
+ # Add project roots to anatomy data
+ template_data["root"] = anatomy.roots
+ template_data["platform"] = platform.system().lower()
+
+ # Add environment variables to template data
+ template_data.update(env)
+
+ # Get config path from core or host settings
+ # - based on override flag in host settings
# TODO: in future rewrite this to be more explicit
override_global_config = host_ocio_config.get("override_global_config")
if override_global_config is None:
- # for already saved overrides from previous version
- # TODO: remove this in future - backward compatibility
- override_global_config = host_ocio_config.get("enabled")
+ override_global_config = host_ocio_config_enabled
- if override_global_config:
- config_data = _get_config_data(
- host_ocio_config["filepath"], formatting_data, env
+ if not override_global_config:
+ config_data = _get_global_config_data(
+ project_name,
+ host_name,
+ anatomy,
+ template_data,
+ imageio_global,
+ folder_id,
+ log,
)
else:
- # get config path from global
- config_global = imageio_global["ocio_config"]
- config_data = _get_config_data(
- config_global["filepath"], formatting_data, env
+ config_data = _get_host_config_data(
+ host_ocio_config["filepath"], template_data
)
if not config_data:
raise FileExistsError(
- "No OCIO config found in settings. It is "
- "either missing or there is typo in path inputs"
+ "No OCIO config found in settings. It is"
+ " either missing or there is typo in path inputs"
)
return config_data
-def _get_config_data(path_list, anatomy_data, env=None):
+def _get_host_config_data(templates, template_data):
"""Return first existing path in path list.
- If template is used in path inputs,
- then it is formatted by anatomy data
- and environment variables
+ Use template data to fill possible formatting in paths.
Args:
- path_list (list[str]): list of abs paths
- anatomy_data (dict): formatting data
- env (Optional[dict]): Environment variables.
+ templates (list[str]): List of templates to config paths.
+ template_data (dict): Template data used to format templates.
Returns:
- dict: config data
+ Union[dict, None]: Config data or 'None' if templates are empty
+ or any path exists.
+
"""
- formatting_data = deepcopy(anatomy_data)
-
- environment_vars = env or dict(**os.environ)
-
- # format the path for potential env vars
- formatting_data.update(environment_vars)
-
- # first try host config paths
- for path_ in path_list:
- formatted_path = _format_path(path_, formatting_data)
-
- if not os.path.exists(formatted_path):
+ for template in templates:
+ formatted_path = StringTemplate.format_template(
+ template, template_data
+ )
+ if not formatted_path.solved:
continue
- return {
- "path": os.path.normpath(formatted_path),
- "template": path_
- }
-
-
-def _format_path(template_path, formatting_data):
- """Single template path formatting.
-
- Args:
- template_path (str): template string
- formatting_data (dict): data to be used for
- template formatting
-
- Returns:
- str: absolute formatted path
- """
- # format path for anatomy keys
- formatted_path = StringTemplate(template_path).format(
- formatting_data)
-
- return os.path.abspath(formatted_path)
+ path = os.path.abspath(formatted_path)
+ if os.path.exists(path):
+ return {
+ "path": os.path.normpath(path),
+ "template": template
+ }
def get_imageio_file_rules(project_name, host_name, project_settings=None):
"""Get ImageIO File rules from project settings
Args:
- project_name (str): project name
- host_name (str): host name
- project_settings (dict, optional): project settings.
- Defaults to None.
+ project_name (str): Project name.
+ host_name (str): Host name.
+ project_settings (Optional[dict]): Project settings.
Returns:
list[dict[str, Any]]: file rules data
+
"""
project_settings = project_settings or get_project_settings(project_name)
@@ -960,7 +1069,7 @@ def get_remapped_colorspace_to_native(
"""Return native colorspace name.
Args:
- ocio_colorspace_name (str | None): ocio colorspace name
+ ocio_colorspace_name (str | None): OCIO colorspace name.
host_name (str): Host name.
imageio_host_settings (dict[str, Any]): ImageIO host settings.
@@ -968,16 +1077,15 @@ def get_remapped_colorspace_to_native(
Union[str, None]: native colorspace name defined in remapping or None
"""
- CachedData.remapping.setdefault(host_name, {})
- if CachedData.remapping[host_name].get("to_native") is None:
+ host_mapping = CachedData.remapping.setdefault(host_name, {})
+ if "to_native" not in host_mapping:
remapping_rules = imageio_host_settings["remapping"]["rules"]
- CachedData.remapping[host_name]["to_native"] = {
+ host_mapping["to_native"] = {
rule["ocio_name"]: rule["host_native_name"]
for rule in remapping_rules
}
- return CachedData.remapping[host_name]["to_native"].get(
- ocio_colorspace_name)
+ return host_mapping["to_native"].get(ocio_colorspace_name)
def get_remapped_colorspace_from_native(
@@ -992,30 +1100,29 @@ def get_remapped_colorspace_from_native(
Returns:
Union[str, None]: Ocio colorspace name defined in remapping or None.
- """
- CachedData.remapping.setdefault(host_name, {})
- if CachedData.remapping[host_name].get("from_native") is None:
+ """
+ host_mapping = CachedData.remapping.setdefault(host_name, {})
+ if "from_native" not in host_mapping:
remapping_rules = imageio_host_settings["remapping"]["rules"]
- CachedData.remapping[host_name]["from_native"] = {
+ host_mapping["from_native"] = {
rule["host_native_name"]: rule["ocio_name"]
for rule in remapping_rules
}
- return CachedData.remapping[host_name]["from_native"].get(
- host_native_colorspace_name)
+ return host_mapping["from_native"].get(host_native_colorspace_name)
def _get_imageio_settings(project_settings, host_name):
"""Get ImageIO settings for global and host
Args:
- project_settings (dict): project settings.
- Defaults to None.
- host_name (str): host name
+ project_settings (dict[str, Any]): Project settings.
+ host_name (str): Host name.
Returns:
- tuple[dict, dict]: image io settings for global and host
+ tuple[dict, dict]: Image io settings for global and host.
+
"""
# get image io from global and host_name
imageio_global = project_settings["core"]["imageio"]
@@ -1033,27 +1140,41 @@ def get_colorspace_settings_from_publish_context(context_data):
Returns:
tuple | bool: config, file rules or None
+
"""
if "imageioSettings" in context_data and context_data["imageioSettings"]:
return context_data["imageioSettings"]
project_name = context_data["projectName"]
+ folder_path = context_data["folderPath"]
+ task_name = context_data["task"]
host_name = context_data["hostName"]
- anatomy_data = context_data["anatomyData"]
- project_settings_ = context_data["project_settings"]
+ anatomy = context_data["anatomy"]
+ template_data = context_data["anatomyData"]
+ project_settings = context_data["project_settings"]
+ folder_id = None
+ folder_entity = context_data.get("folderEntity")
+ if folder_entity:
+ folder_id = folder_entity["id"]
- config_data = get_imageio_config(
- project_name, host_name,
- project_settings=project_settings_,
- anatomy_data=anatomy_data
+ config_data = get_imageio_config_preset(
+ project_name,
+ folder_path,
+ task_name,
+ host_name,
+ anatomy=anatomy,
+ project_settings=project_settings,
+ template_data=template_data,
+ folder_id=folder_id,
)
# caching invalid state, so it's not recalculated all the time
file_rules = None
if config_data:
file_rules = get_imageio_file_rules(
- project_name, host_name,
- project_settings=project_settings_
+ project_name,
+ host_name,
+ project_settings=project_settings
)
# caching settings for future instance processing
@@ -1063,18 +1184,13 @@ def get_colorspace_settings_from_publish_context(context_data):
def set_colorspace_data_to_representation(
- representation, context_data,
+ representation,
+ context_data,
colorspace=None,
log=None
):
"""Sets colorspace data to representation.
- Args:
- representation (dict): publishing representation
- context_data (publish.Context.data): publishing context data
- colorspace (str, optional): colorspace name. Defaults to None.
- log (logging.Logger, optional): logger instance. Defaults to None.
-
Example:
```
{
@@ -1089,6 +1205,12 @@ def set_colorspace_data_to_representation(
}
```
+ Args:
+ representation (dict): publishing representation
+ context_data (publish.Context.data): publishing context data
+ colorspace (Optional[str]): Colorspace name.
+ log (Optional[logging.Logger]): logger instance.
+
"""
log = log or Logger.get_logger(__name__)
@@ -1122,12 +1244,15 @@ def set_colorspace_data_to_representation(
filename = filename[0]
# get matching colorspace from rules
- colorspace = colorspace or get_imageio_colorspace_from_filepath(
- filename, host_name, project_name,
- config_data=config_data,
- file_rules=file_rules,
- project_settings=project_settings
- )
+ if colorspace is None:
+ colorspace = get_imageio_file_rules_colorspace_from_filepath(
+ filename,
+ host_name,
+ project_name,
+ config_data=config_data,
+ file_rules=file_rules,
+ project_settings=project_settings
+ )
# infuse data to representation
if colorspace:
@@ -1149,47 +1274,330 @@ def get_display_view_colorspace_name(config_path, display, view):
view (str): view name e.g. "sRGB"
Returns:
- view color space name (str) e.g. "Output - sRGB"
+ str: View color space name. e.g. "Output - sRGB"
+
"""
-
- if not compatibility_check():
- # python environment is not compatible with PyOpenColorIO
- # needs to be run in subprocess
- return get_display_view_colorspace_subprocess(config_path,
- display, view)
-
- from ayon_core.scripts.ocio_wrapper import _get_display_view_colorspace_name # noqa
-
- return _get_display_view_colorspace_name(config_path, display, view)
+ if has_compatible_ocio_package():
+ return _get_display_view_colorspace_name(
+ config_path, display, view
+ )
+ return _get_wrapped_with_subprocess(
+ "get_display_view_colorspace_name",
+ config_path=config_path,
+ display=display,
+ view=view
+ )
-def get_display_view_colorspace_subprocess(config_path, display, view):
- """Returns the colorspace attribute of the (display, view) pair
- via subprocess.
+# --- Implementation of logic using 'PyOpenColorIO' ---
+def _get_ocio_config(config_path):
+ """Helper function to create OCIO config object.
+
+ Args:
+ config_path (str): Path to config.
+
+ Returns:
+ PyOpenColorIO.Config: OCIO config for the confing path.
+
+ """
+ import PyOpenColorIO
+
+ config_path = os.path.abspath(config_path)
+
+ if not os.path.isfile(config_path):
+ raise IOError("Input path should be `config.ocio` file")
+
+ return PyOpenColorIO.Config.CreateFromFile(config_path)
+
+
+def _get_config_file_rules_colorspace_from_filepath(config_path, filepath):
+ """Return found colorspace data found in v2 file rules.
+
+ Args:
+ config_path (str): path string leading to config.ocio
+ filepath (str): path string leading to v2 file rules
+
+ Raises:
+ IOError: Input config does not exist.
+
+ Returns:
+ dict: aggregated available colorspaces
+
+ """
+ config = _get_ocio_config(config_path)
+
+ # TODO: use `parseColorSpaceFromString` instead if ocio v1
+ return config.getColorSpaceFromFilepath(str(filepath))
+
+
+def _get_config_version_data(config_path):
+ """Return major and minor version info.
+
+ Args:
+ config_path (str): path string leading to config.ocio
+
+ Raises:
+ IOError: Input config does not exist.
+
+ Returns:
+ dict: minor and major keys with values
+
+ """
+ config = _get_ocio_config(config_path)
+
+ return {
+ "major": config.getMajorVersion(),
+ "minor": config.getMinorVersion()
+ }
+
+
+def _get_display_view_colorspace_name(config_path, display, view):
+ """Returns the colorspace attribute of the (display, view) pair.
Args:
config_path (str): path string leading to config.ocio
display (str): display name e.g. "ACES"
view (str): view name e.g. "sRGB"
+ Raises:
+ IOError: Input config does not exist.
+
Returns:
- view color space name (str) e.g. "Output - sRGB"
+ str: view color space name e.g. "Output - sRGB"
+
+ """
+ config = _get_ocio_config(config_path)
+ return config.getDisplayViewColorSpaceName(display, view)
+
+
+def _get_ocio_config_colorspaces(config_path):
+ """Return all found colorspace data.
+
+ Args:
+ config_path (str): path string leading to config.ocio
+
+ Raises:
+ IOError: Input config does not exist.
+
+ Returns:
+ dict: aggregated available colorspaces
+
+ """
+ config = _get_ocio_config(config_path)
+
+ colorspace_data = {
+ "roles": {},
+ "colorspaces": {
+ color.getName(): {
+ "family": color.getFamily(),
+ "categories": list(color.getCategories()),
+ "aliases": list(color.getAliases()),
+ "equalitygroup": color.getEqualityGroup(),
+ }
+ for color in config.getColorSpaces()
+ },
+ "displays_views": {
+ f"{view} ({display})": {
+ "display": display,
+ "view": view
+
+ }
+ for display in config.getDisplays()
+ for view in config.getViews(display)
+ },
+ "looks": {}
+ }
+
+ # add looks
+ looks = config.getLooks()
+ if looks:
+ colorspace_data["looks"] = {
+ look.getName(): {"process_space": look.getProcessSpace()}
+ for look in looks
+ }
+
+ # add roles
+ roles = config.getRoles()
+ if roles:
+ colorspace_data["roles"] = {
+ role: {"colorspace": colorspace}
+ for (role, colorspace) in roles
+ }
+
+ return colorspace_data
+
+
+def _get_ocio_config_views(config_path):
+ """Return all found viewer data.
+
+ Args:
+ config_path (str): path string leading to config.ocio
+
+ Raises:
+ IOError: Input config does not exist.
+
+ Returns:
+ dict: aggregated available viewers
+
+ """
+ config = _get_ocio_config(config_path)
+
+ output = {}
+ for display in config.getDisplays():
+ for view in config.getViews(display):
+ colorspace = config.getDisplayViewColorSpaceName(display, view)
+ # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views # noqa
+ if colorspace == "":
+ colorspace = display
+
+ output[f"{display}/{view}"] = {
+ "display": display,
+ "view": view,
+ "colorspace": colorspace
+ }
+
+ return output
+
+
+# --- Current context functions ---
+def get_current_context_imageio_config_preset(
+ anatomy=None,
+ project_settings=None,
+ template_data=None,
+ env=None,
+):
+ """Get ImageIO config preset for current context.
+
+ Args:
+ anatomy (Optional[Anatomy]): Current project anatomy.
+ project_settings (Optional[dict[str, Any]]): Current project settings.
+ template_data (Optional[dict[str, Any]]): Prepared template data
+ for current context.
+ env (Optional[dict[str, str]]): Custom environment variable values.
+
+ Returns:
+ dict: ImageIO config preset.
+
+ """
+ from .context_tools import get_current_context, get_current_host_name
+
+ context = get_current_context()
+ host_name = get_current_host_name()
+ return get_imageio_config_preset(
+ context["project_name"],
+ context["folder_path"],
+ context["task_name"],
+ host_name,
+ anatomy=anatomy,
+ project_settings=project_settings,
+ template_data=template_data,
+ env=env,
+ )
+
+
+# --- Deprecated functions ---
+@deprecated("has_compatible_ocio_package")
+def compatibility_check():
+ """Making sure PyOpenColorIO is importable
+
+ Deprecated:
+ Deprecated since '0.3.2'. Use `has_compatible_ocio_package` instead.
"""
- with _make_temp_json_file() as tmp_json_path:
- # Prepare subprocess arguments
- args = [
- "run", get_ocio_config_script_path(),
- "config", "get_display_view_colorspace_name",
- "--in_path", config_path,
- "--out_path", tmp_json_path,
- "--display", display,
- "--view", view
- ]
- log.debug("Executing: {}".format(" ".join(args)))
+ return has_compatible_ocio_package()
- run_ayon_launcher_process(*args, logger=log)
- # return default view colorspace name
- with open(tmp_json_path, "r") as f:
- return json.load(f)
+@deprecated("get_imageio_file_rules_colorspace_from_filepath")
+def get_imageio_colorspace_from_filepath(*args, **kwargs):
+ return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs)
+
+
+@deprecated("get_imageio_file_rules_colorspace_from_filepath")
+def get_colorspace_from_filepath(*args, **kwargs):
+ return get_imageio_file_rules_colorspace_from_filepath(*args, **kwargs)
+
+
+@deprecated("_get_wrapped_with_subprocess")
+def get_colorspace_data_subprocess(config_path):
+ """[Deprecated] Get colorspace data via subprocess
+
+ Deprecated:
+ Deprecated since OpenPype. Use `_get_wrapped_with_subprocess` instead.
+
+ Args:
+ config_path (str): path leading to config.ocio file
+
+ Returns:
+ dict: colorspace and family in couple
+ """
+ return _get_wrapped_with_subprocess(
+ "get_ocio_config_colorspaces",
+ config_path=config_path
+ )
+
+
+@deprecated("_get_wrapped_with_subprocess")
+def get_views_data_subprocess(config_path):
+ """[Deprecated] Get viewers data via subprocess
+
+ Deprecated:
+ Deprecated since OpenPype. Use `_get_wrapped_with_subprocess` instead.
+
+ Args:
+ config_path (str): path leading to config.ocio file
+
+ Returns:
+ dict: `display/viewer` and viewer data
+
+ """
+ return _get_wrapped_with_subprocess(
+ "get_ocio_config_views",
+ config_path=config_path
+ )
+
+
+@deprecated("get_imageio_config_preset")
+def get_imageio_config(
+ project_name,
+ host_name,
+ project_settings=None,
+ anatomy_data=None,
+ anatomy=None,
+ env=None
+):
+ """Returns config data from settings
+
+ Config path is formatted in `path` key
+ and original settings input is saved into `template` key.
+
+ Deprecated:
+ Deprecated since '0.3.1' . Use `get_imageio_config_preset` instead.
+
+ Args:
+ project_name (str): project name
+ host_name (str): host name
+ project_settings (Optional[dict]): Project settings.
+ anatomy_data (Optional[dict]): anatomy formatting data.
+ anatomy (Optional[Anatomy]): Anatomy object.
+ env (Optional[dict]): Environment variables.
+
+ Returns:
+ dict: config path data or empty dict
+
+ """
+ if not anatomy_data:
+ from .context_tools import get_current_context_template_data
+ anatomy_data = get_current_context_template_data()
+
+ task_name = anatomy_data.get("task", {}).get("name")
+ folder_path = anatomy_data.get("folder", {}).get("path")
+ return get_imageio_config_preset(
+ project_name,
+ folder_path,
+ task_name,
+ host_name,
+ anatomy=anatomy,
+ project_settings=project_settings,
+ template_data=anatomy_data,
+ env=env,
+ )
diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py
index 33567d7280..c32d04c44c 100644
--- a/client/ayon_core/pipeline/context_tools.py
+++ b/client/ayon_core/pipeline/context_tools.py
@@ -459,36 +459,6 @@ def is_representation_from_latest(representation):
)
-def get_template_data_from_session(session=None, settings=None):
- """Template data for template fill from session keys.
-
- Args:
- session (Union[Dict[str, str], None]): The Session to use. If not
- provided use the currently active global Session.
- settings (Optional[Dict[str, Any]]): Prepared studio or project
- settings.
-
- Returns:
- Dict[str, Any]: All available data from session.
- """
-
- if session is not None:
- project_name = session["AYON_PROJECT_NAME"]
- folder_path = session["AYON_FOLDER_PATH"]
- task_name = session["AYON_TASK_NAME"]
- host_name = session["AYON_HOST_NAME"]
- else:
- context = get_current_context()
- project_name = context["project_name"]
- folder_path = context["folder_path"]
- task_name = context["task_name"]
- host_name = get_current_host_name()
-
- return get_template_data_with_names(
- project_name, folder_path, task_name, host_name, settings
- )
-
-
def get_current_context_template_data(settings=None):
"""Prepare template data for current context.
diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py
index b8618738fb..45846553a4 100644
--- a/client/ayon_core/pipeline/create/context.py
+++ b/client/ayon_core/pipeline/create/context.py
@@ -681,7 +681,7 @@ class PublishAttributeValues(AttributeValues):
@property
def parent(self):
- self.publish_attributes.parent
+ return self.publish_attributes.parent
class PublishAttributes:
@@ -1987,12 +1987,12 @@ class CreateContext:
"Folder '{}' was not found".format(folder_path)
)
- task_name = None
if task_entity is None:
- task_name = self.get_current_task_name()
- task_entity = ayon_api.get_task_by_name(
- project_name, folder_entity["id"], task_name
- )
+ current_task_name = self.get_current_task_name()
+ if current_task_name:
+ task_entity = ayon_api.get_task_by_name(
+ project_name, folder_entity["id"], current_task_name
+ )
if pre_create_data is None:
pre_create_data = {}
@@ -2018,7 +2018,7 @@ class CreateContext:
instance_data = {
"folderPath": folder_entity["path"],
- "task": task_name,
+ "task": task_entity["name"] if task_entity else None,
"productType": creator.product_type,
"variant": variant
}
@@ -2053,7 +2053,7 @@ class CreateContext:
exc_info = sys.exc_info()
self.log.warning(error_message.format(identifier, exc_info[1]))
- except:
+ except: # noqa: E722
add_traceback = True
exc_info = sys.exc_info()
self.log.warning(
@@ -2163,7 +2163,7 @@ class CreateContext:
exc_info = sys.exc_info()
self.log.warning(error_message.format(identifier, exc_info[1]))
- except:
+ except: # noqa: E722
failed = True
add_traceback = True
exc_info = sys.exc_info()
@@ -2197,7 +2197,7 @@ class CreateContext:
try:
convertor.find_instances()
- except:
+ except: # noqa: E722
failed_info.append(
prepare_failed_convertor_operation_info(
convertor.identifier, sys.exc_info()
@@ -2373,7 +2373,7 @@ class CreateContext:
exc_info = sys.exc_info()
self.log.warning(error_message.format(identifier, exc_info[1]))
- except:
+ except: # noqa: E722
failed = True
add_traceback = True
exc_info = sys.exc_info()
@@ -2440,7 +2440,7 @@ class CreateContext:
error_message.format(identifier, exc_info[1])
)
- except:
+ except: # noqa: E722
failed = True
add_traceback = True
exc_info = sys.exc_info()
@@ -2546,7 +2546,7 @@ class CreateContext:
try:
self.run_convertor(convertor_identifier)
- except:
+ except: # noqa: E722
failed_info.append(
prepare_failed_convertor_operation_info(
convertor_identifier, sys.exc_info()
diff --git a/client/ayon_core/pipeline/publish/abstract_collect_render.py b/client/ayon_core/pipeline/publish/abstract_collect_render.py
index c50dc16380..17cab876b6 100644
--- a/client/ayon_core/pipeline/publish/abstract_collect_render.py
+++ b/client/ayon_core/pipeline/publish/abstract_collect_render.py
@@ -80,6 +80,7 @@ class RenderInstance(object):
anatomyData = attr.ib(default=None)
outputDir = attr.ib(default=None)
context = attr.ib(default=None)
+ deadline = attr.ib(default=None)
# The source instance the data of this render instance should merge into
source_instance = attr.ib(default=None, type=pyblish.api.Instance)
@@ -215,13 +216,12 @@ class AbstractCollectRender(pyblish.api.ContextPlugin):
# add additional data
data = self.add_additional_data(data)
- render_instance_dict = attr.asdict(render_instance)
- # Merge into source instance if provided, otherwise create instance
- instance = render_instance_dict.pop("source_instance", None)
+ instance = render_instance.source_instance
if instance is None:
instance = context.create_instance(render_instance.name)
+ render_instance_dict = attr.asdict(render_instance)
instance.data.update(render_instance_dict)
instance.data.update(data)
diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py
index 8d3644637b..7f63089d33 100644
--- a/client/ayon_core/pipeline/publish/lib.py
+++ b/client/ayon_core/pipeline/publish/lib.py
@@ -336,17 +336,16 @@ def get_plugin_settings(plugin, project_settings, log, category=None):
settings_category = getattr(plugin, "settings_category", None)
if settings_category:
try:
- return (
- project_settings
- [settings_category]
- ["publish"]
- [plugin.__name__]
- )
+ category_settings = project_settings[settings_category]
except KeyError:
log.warning((
- "Couldn't find plugin '{}' settings"
- " under settings category '{}'"
- ).format(plugin.__name__, settings_category))
+ "Couldn't find settings category '{}' in project settings"
+ ).format(settings_category))
+ return {}
+
+ try:
+ return category_settings["publish"][plugin.__name__]
+ except KeyError:
return {}
# Use project settings based on a category name
diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py
index 526c7d35c5..d5f06d6a59 100644
--- a/client/ayon_core/pipeline/template_data.py
+++ b/client/ayon_core/pipeline/template_data.py
@@ -73,8 +73,8 @@ def get_folder_template_data(folder_entity, project_name):
- 'parent' - direct parent name, project name used if is under
project
- Required document fields:
- Folder: 'path' -> Plan to require: 'folderType'
+ Required entity fields:
+ Folder: 'path', 'folderType'
Args:
folder_entity (Dict[str, Any]): Folder entity.
@@ -101,6 +101,8 @@ def get_folder_template_data(folder_entity, project_name):
return {
"folder": {
"name": folder_name,
+ "type": folder_entity["folderType"],
+ "path": path,
},
"asset": folder_name,
"hierarchy": hierarchy,
diff --git a/client/ayon_core/pipeline/thumbnails.py b/client/ayon_core/pipeline/thumbnails.py
new file mode 100644
index 0000000000..dbb38615d8
--- /dev/null
+++ b/client/ayon_core/pipeline/thumbnails.py
@@ -0,0 +1,263 @@
+import os
+import time
+import collections
+
+import ayon_api
+
+from ayon_core.lib.local_settings import get_ayon_appdirs
+
+
+FileInfo = collections.namedtuple(
+ "FileInfo",
+ ("path", "size", "modification_time")
+)
+
+
+class ThumbnailsCache:
+ """Cache of thumbnails on local storage.
+
+ Thumbnails are cached to appdirs to predefined directory. Each project has
+ own subfolder with thumbnails -> that's because each project has own
+ thumbnail id validation and file names are thumbnail ids with matching
+ extension. Extensions are predefined (.png and .jpeg).
+
+ Cache has cleanup mechanism which is triggered on initialized by default.
+
+ The cleanup has 2 levels:
+ 1. soft cleanup which remove all files that are older then 'days_alive'
+ 2. max size cleanup which remove all files until the thumbnails folder
+ contains less then 'max_filesize'
+ - this is time consuming so it's not triggered automatically
+
+ Args:
+ cleanup (bool): Trigger soft cleanup (Cleanup expired thumbnails).
+ """
+
+ # Lifetime of thumbnails (in seconds)
+ # - default 3 days
+ days_alive = 3
+ # Max size of thumbnail directory (in bytes)
+ # - default 2 Gb
+ max_filesize = 2 * 1024 * 1024 * 1024
+
+ def __init__(self, cleanup=True):
+ self._thumbnails_dir = None
+ self._days_alive_secs = self.days_alive * 24 * 60 * 60
+ if cleanup:
+ self.cleanup()
+
+ def get_thumbnails_dir(self):
+ """Root directory where thumbnails are stored.
+
+ Returns:
+ str: Path to thumbnails root.
+ """
+
+ if self._thumbnails_dir is None:
+ self._thumbnails_dir = get_ayon_appdirs("thumbnails")
+ return self._thumbnails_dir
+
+ thumbnails_dir = property(get_thumbnails_dir)
+
+ def get_thumbnails_dir_file_info(self):
+ """Get information about all files in thumbnails directory.
+
+ Returns:
+ List[FileInfo]: List of file information about all files.
+ """
+
+ thumbnails_dir = self.thumbnails_dir
+ files_info = []
+ if not os.path.exists(thumbnails_dir):
+ return files_info
+
+ for root, _, filenames in os.walk(thumbnails_dir):
+ for filename in filenames:
+ path = os.path.join(root, filename)
+ files_info.append(FileInfo(
+ path, os.path.getsize(path), os.path.getmtime(path)
+ ))
+ return files_info
+
+ def get_thumbnails_dir_size(self, files_info=None):
+ """Got full size of thumbnail directory.
+
+ Args:
+ files_info (List[FileInfo]): Prepared file information about
+ files in thumbnail directory.
+
+ Returns:
+ int: File size of all files in thumbnail directory.
+ """
+
+ if files_info is None:
+ files_info = self.get_thumbnails_dir_file_info()
+
+ if not files_info:
+ return 0
+
+ return sum(
+ file_info.size
+ for file_info in files_info
+ )
+
+ def cleanup(self, check_max_size=False):
+ """Cleanup thumbnails directory.
+
+ Args:
+ check_max_size (bool): Also cleanup files to match max size of
+ thumbnails directory.
+ """
+
+ thumbnails_dir = self.get_thumbnails_dir()
+ # Skip if thumbnails dir does not exist yet
+ if not os.path.exists(thumbnails_dir):
+ return
+
+ self._soft_cleanup(thumbnails_dir)
+ if check_max_size:
+ self._max_size_cleanup(thumbnails_dir)
+
+ def _soft_cleanup(self, thumbnails_dir):
+ current_time = time.time()
+ for root, _, filenames in os.walk(thumbnails_dir):
+ for filename in filenames:
+ path = os.path.join(root, filename)
+ modification_time = os.path.getmtime(path)
+ if current_time - modification_time > self._days_alive_secs:
+ os.remove(path)
+
+ def _max_size_cleanup(self, thumbnails_dir):
+ files_info = self.get_thumbnails_dir_file_info()
+ size = self.get_thumbnails_dir_size(files_info)
+ if size < self.max_filesize:
+ return
+
+ sorted_file_info = collections.deque(
+ sorted(files_info, key=lambda item: item.modification_time)
+ )
+ diff = size - self.max_filesize
+ while diff > 0:
+ if not sorted_file_info:
+ break
+
+ file_info = sorted_file_info.popleft()
+ diff -= file_info.size
+ os.remove(file_info.path)
+
+ def get_thumbnail_filepath(self, project_name, thumbnail_id):
+ """Get thumbnail by thumbnail id.
+
+ Args:
+ project_name (str): Name of project.
+ thumbnail_id (str): Thumbnail id.
+
+ Returns:
+ Union[str, None]: Path to thumbnail image or None if thumbnail
+ is not cached yet.
+ """
+
+ if not thumbnail_id:
+ return None
+
+ for ext in (
+ ".png",
+ ".jpeg",
+ ):
+ filepath = os.path.join(
+ self.thumbnails_dir, project_name, thumbnail_id + ext
+ )
+ if os.path.exists(filepath):
+ return filepath
+ return None
+
+ def get_project_dir(self, project_name):
+ """Path to root directory for specific project.
+
+ Args:
+ project_name (str): Name of project for which root directory path
+ should be returned.
+
+ Returns:
+ str: Path to root of project's thumbnails.
+ """
+
+ return os.path.join(self.thumbnails_dir, project_name)
+
+ def make_sure_project_dir_exists(self, project_name):
+ project_dir = self.get_project_dir(project_name)
+ if not os.path.exists(project_dir):
+ os.makedirs(project_dir)
+ return project_dir
+
+ def store_thumbnail(self, project_name, thumbnail_id, content, mime_type):
+ """Store thumbnail to cache folder.
+
+ Args:
+ project_name (str): Project where the thumbnail belong to.
+ thumbnail_id (str): Thumbnail id.
+ content (bytes): Byte content of thumbnail file.
+ mime_type (str): Type of content.
+
+ Returns:
+ str: Path to cached thumbnail image file.
+ """
+
+ if mime_type == "image/png":
+ ext = ".png"
+ elif mime_type == "image/jpeg":
+ ext = ".jpeg"
+ else:
+ raise ValueError(
+ "Unknown mime type for thumbnail \"{}\"".format(mime_type))
+
+ project_dir = self.make_sure_project_dir_exists(project_name)
+ thumbnail_path = os.path.join(project_dir, thumbnail_id + ext)
+ with open(thumbnail_path, "wb") as stream:
+ stream.write(content)
+
+ current_time = time.time()
+ os.utime(thumbnail_path, (current_time, current_time))
+
+ return thumbnail_path
+
+
+class _CacheItems:
+ thumbnails_cache = ThumbnailsCache()
+
+
+def get_thumbnail_path(project_name, thumbnail_id):
+ """Get path to thumbnail image.
+
+ Args:
+ project_name (str): Project where thumbnail belongs to.
+ thumbnail_id (Union[str, None]): Thumbnail id.
+
+ Returns:
+ Union[str, None]: Path to thumbnail image or None if thumbnail
+ id is not valid or thumbnail was not possible to receive.
+
+ """
+ if not thumbnail_id:
+ return None
+
+ filepath = _CacheItems.thumbnails_cache.get_thumbnail_filepath(
+ project_name, thumbnail_id
+ )
+ if filepath is not None:
+ return filepath
+
+ # 'ayon_api' had a bug, public function
+ # 'get_thumbnail_by_id' did not return output of
+ # 'ServerAPI' method.
+ con = ayon_api.get_server_api_connection()
+ result = con.get_thumbnail_by_id(project_name, thumbnail_id)
+
+ if result is not None and result.is_valid:
+ return _CacheItems.thumbnails_cache.store_thumbnail(
+ project_name,
+ thumbnail_id,
+ result.content,
+ result.content_type
+ )
+ return None
diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py
index f8cc81e718..ad5a5d43fc 100644
--- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py
+++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py
@@ -33,6 +33,7 @@ import collections
import pyblish.api
import ayon_api
+from ayon_core.pipeline.template_data import get_folder_template_data
from ayon_core.pipeline.version_start import get_versioning_start
@@ -383,24 +384,11 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
# - 'folder', 'hierarchy', 'parent', 'folder'
folder_entity = instance.data.get("folderEntity")
if folder_entity:
- folder_name = folder_entity["name"]
- folder_path = folder_entity["path"]
- hierarchy_parts = folder_path.split("/")
- hierarchy_parts.pop(0)
- hierarchy_parts.pop(-1)
- parent_name = project_entity["name"]
- if hierarchy_parts:
- parent_name = hierarchy_parts[-1]
-
- hierarchy = "/".join(hierarchy_parts)
- anatomy_data.update({
- "asset": folder_name,
- "hierarchy": hierarchy,
- "parent": parent_name,
- "folder": {
- "name": folder_name,
- },
- })
+ folder_data = get_folder_template_data(
+ folder_entity,
+ project_entity["name"]
+ )
+ anatomy_data.update(folder_data)
return
if instance.data.get("newAssetPublishing"):
@@ -418,6 +406,11 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
"parent": parent_name,
"folder": {
"name": folder_name,
+ "path": instance.data["folderPath"],
+ # TODO get folder type from hierarchy
+ # Using 'Shot' is current default behavior of editorial
+ # (or 'newAssetPublishing') publishing.
+ "type": "Shot",
},
})
diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py
index 764168edd3..865b566e6e 100644
--- a/client/ayon_core/plugins/publish/integrate.py
+++ b/client/ayon_core/plugins/publish/integrate.py
@@ -42,7 +42,7 @@ def prepare_changes(old_entity, new_entity):
Returns:
dict[str, Any]: Changes that have new entity.
-
+
"""
changes = {}
for key in set(new_entity.keys()):
@@ -108,68 +108,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
label = "Integrate Asset"
order = pyblish.api.IntegratorOrder
- families = ["workfile",
- "pointcache",
- "pointcloud",
- "proxyAbc",
- "camera",
- "animation",
- "model",
- "maxScene",
- "mayaAscii",
- "mayaScene",
- "setdress",
- "layout",
- "ass",
- "vdbcache",
- "scene",
- "vrayproxy",
- "vrayscene_layer",
- "render",
- "prerender",
- "imagesequence",
- "review",
- "rendersetup",
- "rig",
- "plate",
- "look",
- "ociolook",
- "audio",
- "yetiRig",
- "yeticache",
- "nukenodes",
- "gizmo",
- "source",
- "matchmove",
- "image",
- "assembly",
- "fbx",
- "gltf",
- "textures",
- "action",
- "harmony.template",
- "harmony.palette",
- "editorial",
- "background",
- "camerarig",
- "redshiftproxy",
- "effect",
- "xgen",
- "hda",
- "usd",
- "staticMesh",
- "skeletalMesh",
- "mvLook",
- "mvUsd",
- "mvUsdComposition",
- "mvUsdOverride",
- "online",
- "uasset",
- "blendScene",
- "yeticacheUE",
- "tycache",
- "csv_ingest_file",
- ]
default_template_name = "publish"
@@ -359,7 +297,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
# Compute the resource file infos once (files belonging to the
# version instance instead of an individual representation) so
- # we can re-use those file infos per representation
+ # we can reuse those file infos per representation
resource_file_infos = self.get_files_info(
resource_destinations, anatomy
)
diff --git a/client/ayon_core/plugins/publish/validate_containers.py b/client/ayon_core/plugins/publish/validate_containers.py
index bd21ec9693..520e7a7ce9 100644
--- a/client/ayon_core/plugins/publish/validate_containers.py
+++ b/client/ayon_core/plugins/publish/validate_containers.py
@@ -1,6 +1,11 @@
import pyblish.api
+
+from ayon_core.lib import filter_profiles
+from ayon_core.host import ILoadHost
from ayon_core.pipeline.load import any_outdated_containers
from ayon_core.pipeline import (
+ get_current_host_name,
+ registered_host,
PublishXmlValidationError,
OptionalPyblishPluginMixin
)
@@ -18,17 +23,50 @@ class ShowInventory(pyblish.api.Action):
host_tools.show_scene_inventory()
-class ValidateContainers(OptionalPyblishPluginMixin,
- pyblish.api.ContextPlugin):
-
+class ValidateOutdatedContainers(
+ OptionalPyblishPluginMixin,
+ pyblish.api.ContextPlugin
+):
"""Containers are must be updated to latest version on publish."""
label = "Validate Outdated Containers"
order = pyblish.api.ValidatorOrder
- hosts = ["maya", "houdini", "nuke", "harmony", "photoshop", "aftereffects"]
+
optional = True
actions = [ShowInventory]
+ @classmethod
+ def apply_settings(cls, settings):
+ # Disable plugin if host does not inherit from 'ILoadHost'
+ # - not a host that can load containers
+ host = registered_host()
+ if not isinstance(host, ILoadHost):
+ cls.enabled = False
+ return
+
+ # Disable if no profile is found for the current host
+ profiles = (
+ settings
+ ["core"]
+ ["publish"]
+ ["ValidateOutdatedContainers"]
+ ["plugin_state_profiles"]
+ )
+ profile = filter_profiles(
+ profiles, {"host_names": get_current_host_name()}
+ )
+ if not profile:
+ cls.enabled = False
+ return
+
+ # Apply settings from profile
+ for attr_name in {
+ "enabled",
+ "optional",
+ "active",
+ }:
+ setattr(cls, attr_name, profile[attr_name])
+
def process(self, context):
if not self.is_active(context.data):
return
diff --git a/client/ayon_core/plugins/publish/validate_version.py b/client/ayon_core/plugins/publish/validate_version.py
index 9031194e8c..0359f8fb53 100644
--- a/client/ayon_core/plugins/publish/validate_version.py
+++ b/client/ayon_core/plugins/publish/validate_version.py
@@ -1,8 +1,14 @@
import pyblish.api
-from ayon_core.pipeline.publish import PublishValidationError
+
+from ayon_core.lib import filter_profiles
+from ayon_core.pipeline.publish import (
+ PublishValidationError,
+ OptionalPyblishPluginMixin
+)
+from ayon_core.pipeline import get_current_host_name
-class ValidateVersion(pyblish.api.InstancePlugin):
+class ValidateVersion(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin):
"""Validate instance version.
AYON does not allow overwriting previously published versions.
@@ -11,13 +17,39 @@ class ValidateVersion(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
label = "Validate Version"
- hosts = ["nuke", "maya", "houdini", "blender",
- "photoshop", "aftereffects"]
optional = False
active = True
+ @classmethod
+ def apply_settings(cls, settings):
+ # Disable if no profile is found for the current host
+ profiles = (
+ settings
+ ["core"]
+ ["publish"]
+ ["ValidateVersion"]
+ ["plugin_state_profiles"]
+ )
+ profile = filter_profiles(
+ profiles, {"host_names": get_current_host_name()}
+ )
+ if not profile:
+ cls.enabled = False
+ return
+
+ # Apply settings from profile
+ for attr_name in {
+ "enabled",
+ "optional",
+ "active",
+ }:
+ setattr(cls, attr_name, profile[attr_name])
+
def process(self, instance):
+ if not self.is_active(instance.data):
+ return
+
version = instance.data.get("version")
latest_version = instance.data.get("latestVersion")
diff --git a/client/ayon_core/scripts/ocio_wrapper.py b/client/ayon_core/scripts/ocio_wrapper.py
index 0a78e33c1f..0414fc59ce 100644
--- a/client/ayon_core/scripts/ocio_wrapper.py
+++ b/client/ayon_core/scripts/ocio_wrapper.py
@@ -1,28 +1,31 @@
"""OpenColorIO Wrapper.
-Only to be interpreted by Python 3. It is run in subprocess in case
-Python 2 hosts needs to use it. Or it is used as module for Python 3
-processing.
-
-Providing functionality:
-- get_colorspace - console command - python 2
- - returning all available color spaces
- found in input config path.
-- _get_colorspace_data - python 3 - module function
- - returning all available colorspaces
- found in input config path.
-- get_views - console command - python 2
- - returning all available viewers
- found in input config path.
-- _get_views_data - python 3 - module function
- - returning all available viewers
- found in input config path.
+Receive OpenColorIO information and store it in JSON format for processed
+that don't have access to OpenColorIO or their version of OpenColorIO is
+not compatible.
"""
-import click
import json
from pathlib import Path
-import PyOpenColorIO as ocio
+
+import click
+
+from ayon_core.pipeline.colorspace import (
+ has_compatible_ocio_package,
+ get_display_view_colorspace_name,
+ get_config_file_rules_colorspace_from_filepath,
+ get_config_version_data,
+ get_ocio_config_views,
+ get_ocio_config_colorspaces,
+)
+
+
+def _save_output_to_json_file(output, output_path):
+ json_path = Path(output_path)
+ with open(json_path, "w") as stream:
+ json.dump(output, stream)
+
+ print(f"Data are saved to '{json_path}'")
@click.group()
@@ -30,404 +33,185 @@ def main():
pass # noqa: WPS100
-@main.group()
-def config():
- """Config related commands group
-
- Example of use:
- > pyton.exe ./ocio_wrapper.py config *args
- """
- pass # noqa: WPS100
-
-
-@main.group()
-def colorspace():
- """Colorspace related commands group
-
- Example of use:
- > pyton.exe ./ocio_wrapper.py config *args
- """
- pass # noqa: WPS100
-
-
-@config.command(
- name="get_colorspace",
- help=(
- "return all colorspaces from config file "
- "--path input arg is required"
- )
-)
-@click.option("--in_path", required=True,
- help="path where to read ocio config file",
- type=click.Path(exists=True))
-@click.option("--out_path", required=True,
- help="path where to write output json file",
- type=click.Path())
-def get_colorspace(in_path, out_path):
+@main.command(
+ name="get_ocio_config_colorspaces",
+ help="return all colorspaces from config file")
+@click.option(
+ "--config_path",
+ required=True,
+ help="OCIO config path to read ocio config file.",
+ type=click.Path(exists=True))
+@click.option(
+ "--output_path",
+ required=True,
+ help="path where to write output json file",
+ type=click.Path())
+def _get_ocio_config_colorspaces(config_path, output_path):
"""Aggregate all colorspace to file.
- Python 2 wrapped console command
-
Args:
- in_path (str): config file path string
- out_path (str): temp json file path string
+ config_path (str): config file path string
+ output_path (str): temp json file path string
Example of use:
> pyton.exe ./ocio_wrapper.py config get_colorspace
- --in_path= --out_path=
+ --config_path --output_path
"""
- json_path = Path(out_path)
-
- out_data = _get_colorspace_data(in_path)
-
- with open(json_path, "w") as f_:
- json.dump(out_data, f_)
-
- print(f"Colorspace data are saved to '{json_path}'")
-
-
-def _get_colorspace_data(config_path):
- """Return all found colorspace data.
-
- Args:
- config_path (str): path string leading to config.ocio
-
- Raises:
- IOError: Input config does not exist.
-
- Returns:
- dict: aggregated available colorspaces
- """
- config_path = Path(config_path)
-
- if not config_path.is_file():
- raise IOError(
- f"Input path `{config_path}` should be `config.ocio` file")
-
- config = ocio.Config().CreateFromFile(str(config_path))
-
- colorspace_data = {
- "roles": {},
- "colorspaces": {
- color.getName(): {
- "family": color.getFamily(),
- "categories": list(color.getCategories()),
- "aliases": list(color.getAliases()),
- "equalitygroup": color.getEqualityGroup(),
- }
- for color in config.getColorSpaces()
- },
- "displays_views": {
- f"{view} ({display})": {
- "display": display,
- "view": view
-
- }
- for display in config.getDisplays()
- for view in config.getViews(display)
- },
- "looks": {}
- }
-
- # add looks
- looks = config.getLooks()
- if looks:
- colorspace_data["looks"] = {
- look.getName(): {"process_space": look.getProcessSpace()}
- for look in looks
- }
-
- # add roles
- roles = config.getRoles()
- if roles:
- colorspace_data["roles"] = {
- role: {"colorspace": colorspace}
- for (role, colorspace) in roles
- }
-
- return colorspace_data
-
-
-@config.command(
- name="get_views",
- help=(
- "return all viewers from config file "
- "--path input arg is required"
+ _save_output_to_json_file(
+ get_ocio_config_colorspaces(config_path),
+ output_path
)
-)
-@click.option("--in_path", required=True,
- help="path where to read ocio config file",
- type=click.Path(exists=True))
-@click.option("--out_path", required=True,
- help="path where to write output json file",
- type=click.Path())
-def get_views(in_path, out_path):
+
+
+@main.command(
+ name="get_ocio_config_views",
+ help="All viewers from config file")
+@click.option(
+ "--config_path",
+ required=True,
+ help="OCIO config path to read ocio config file.",
+ type=click.Path(exists=True))
+@click.option(
+ "--output_path",
+ required=True,
+ help="path where to write output json file",
+ type=click.Path())
+def _get_ocio_config_views(config_path, output_path):
"""Aggregate all viewers to file.
- Python 2 wrapped console command
-
Args:
- in_path (str): config file path string
- out_path (str): temp json file path string
+ config_path (str): config file path string
+ output_path (str): temp json file path string
Example of use:
> pyton.exe ./ocio_wrapper.py config get_views \
- --in_path= --out_path=
+ --config_path --output
"""
- json_path = Path(out_path)
-
- out_data = _get_views_data(in_path)
-
- with open(json_path, "w") as f_:
- json.dump(out_data, f_)
-
- print(f"Viewer data are saved to '{json_path}'")
-
-
-def _get_views_data(config_path):
- """Return all found viewer data.
-
- Args:
- config_path (str): path string leading to config.ocio
-
- Raises:
- IOError: Input config does not exist.
-
- Returns:
- dict: aggregated available viewers
- """
- config_path = Path(config_path)
-
- if not config_path.is_file():
- raise IOError("Input path should be `config.ocio` file")
-
- config = ocio.Config().CreateFromFile(str(config_path))
-
- data_ = {}
- for display in config.getDisplays():
- for view in config.getViews(display):
- colorspace = config.getDisplayViewColorSpaceName(display, view)
- # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views # noqa
- if colorspace == "":
- colorspace = display
-
- data_[f"{display}/{view}"] = {
- "display": display,
- "view": view,
- "colorspace": colorspace
- }
-
- return data_
-
-
-@config.command(
- name="get_version",
- help=(
- "return major and minor version from config file "
- "--config_path input arg is required"
- "--out_path input arg is required"
+ _save_output_to_json_file(
+ get_ocio_config_views(config_path),
+ output_path
)
-)
-@click.option("--config_path", required=True,
- help="path where to read ocio config file",
- type=click.Path(exists=True))
-@click.option("--out_path", required=True,
- help="path where to write output json file",
- type=click.Path())
-def get_version(config_path, out_path):
- """Get version of config.
- Python 2 wrapped console command
+
+@main.command(
+ name="get_config_version_data",
+ help="Get major and minor version from config file")
+@click.option(
+ "--config_path",
+ required=True,
+ help="OCIO config path to read ocio config file.",
+ type=click.Path(exists=True))
+@click.option(
+ "--output_path",
+ required=True,
+ help="path where to write output json file",
+ type=click.Path())
+def _get_config_version_data(config_path, output_path):
+ """Get version of config.
Args:
config_path (str): ocio config file path string
- out_path (str): temp json file path string
+ output_path (str): temp json file path string
Example of use:
> pyton.exe ./ocio_wrapper.py config get_version \
- --config_path= --out_path=
+ --config_path --output_path
"""
- json_path = Path(out_path)
-
- out_data = _get_version_data(config_path)
-
- with open(json_path, "w") as f_:
- json.dump(out_data, f_)
-
- print(f"Config version data are saved to '{json_path}'")
-
-
-def _get_version_data(config_path):
- """Return major and minor version info.
-
- Args:
- config_path (str): path string leading to config.ocio
-
- Raises:
- IOError: Input config does not exist.
-
- Returns:
- dict: minor and major keys with values
- """
- config_path = Path(config_path)
-
- if not config_path.is_file():
- raise IOError("Input path should be `config.ocio` file")
-
- config = ocio.Config().CreateFromFile(str(config_path))
-
- return {
- "major": config.getMajorVersion(),
- "minor": config.getMinorVersion()
- }
-
-
-@colorspace.command(
- name="get_config_file_rules_colorspace_from_filepath",
- help=(
- "return colorspace from filepath "
- "--config_path - ocio config file path (input arg is required) "
- "--filepath - any file path (input arg is required) "
- "--out_path - temp json file path (input arg is required)"
+ _save_output_to_json_file(
+ get_config_version_data(config_path),
+ output_path
)
-)
-@click.option("--config_path", required=True,
- help="path where to read ocio config file",
- type=click.Path(exists=True))
-@click.option("--filepath", required=True,
- help="path to file to get colorspace from",
- type=click.Path())
-@click.option("--out_path", required=True,
- help="path where to write output json file",
- type=click.Path())
-def get_config_file_rules_colorspace_from_filepath(
- config_path, filepath, out_path
+
+
+@main.command(
+ name="get_config_file_rules_colorspace_from_filepath",
+ help="Colorspace file rules from filepath")
+@click.option(
+ "--config_path",
+ required=True,
+ help="OCIO config path to read ocio config file.",
+ type=click.Path(exists=True))
+@click.option(
+ "--filepath",
+ required=True,
+ help="Path to file to get colorspace from.",
+ type=click.Path())
+@click.option(
+ "--output_path",
+ required=True,
+ help="Path where to write output json file.",
+ type=click.Path())
+def _get_config_file_rules_colorspace_from_filepath(
+ config_path, filepath, output_path
):
"""Get colorspace from file path wrapper.
- Python 2 wrapped console command
-
Args:
config_path (str): config file path string
filepath (str): path string leading to file
- out_path (str): temp json file path string
+ output_path (str): temp json file path string
Example of use:
- > pyton.exe ./ocio_wrapper.py \
+ > python.exe ./ocio_wrapper.py \
colorspace get_config_file_rules_colorspace_from_filepath \
- --config_path= --filepath= --out_path=
+ --config_path --filepath --output_path
"""
- json_path = Path(out_path)
-
- colorspace = _get_config_file_rules_colorspace_from_filepath(
- config_path, filepath)
-
- with open(json_path, "w") as f_:
- json.dump(colorspace, f_)
-
- print(f"Colorspace name is saved to '{json_path}'")
+ _save_output_to_json_file(
+ get_config_file_rules_colorspace_from_filepath(config_path, filepath),
+ output_path
+ )
-def _get_config_file_rules_colorspace_from_filepath(config_path, filepath):
- """Return found colorspace data found in v2 file rules.
-
- Args:
- config_path (str): path string leading to config.ocio
- filepath (str): path string leading to v2 file rules
-
- Raises:
- IOError: Input config does not exist.
-
- Returns:
- dict: aggregated available colorspaces
- """
- config_path = Path(config_path)
-
- if not config_path.is_file():
- raise IOError(
- f"Input path `{config_path}` should be `config.ocio` file")
-
- config = ocio.Config().CreateFromFile(str(config_path))
-
- # TODO: use `parseColorSpaceFromString` instead if ocio v1
- colorspace = config.getColorSpaceFromFilepath(str(filepath))
-
- return colorspace
-
-
-def _get_display_view_colorspace_name(config_path, display, view):
- """Returns the colorspace attribute of the (display, view) pair.
-
- Args:
- config_path (str): path string leading to config.ocio
- display (str): display name e.g. "ACES"
- view (str): view name e.g. "sRGB"
-
-
- Raises:
- IOError: Input config does not exist.
-
- Returns:
- view color space name (str) e.g. "Output - sRGB"
- """
-
- config_path = Path(config_path)
-
- if not config_path.is_file():
- raise IOError("Input path should be `config.ocio` file")
-
- config = ocio.Config.CreateFromFile(str(config_path))
- colorspace = config.getDisplayViewColorSpaceName(display, view)
-
- return colorspace
-
-
-@config.command(
+@main.command(
name="get_display_view_colorspace_name",
help=(
- "return default view colorspace name "
- "for the given display and view "
- "--path input arg is required"
- )
-)
-@click.option("--in_path", required=True,
- help="path where to read ocio config file",
- type=click.Path(exists=True))
-@click.option("--out_path", required=True,
- help="path where to write output json file",
- type=click.Path())
-@click.option("--display", required=True,
- help="display name",
- type=click.STRING)
-@click.option("--view", required=True,
- help="view name",
- type=click.STRING)
-def get_display_view_colorspace_name(in_path, out_path,
- display, view):
+ "Default view colorspace name for the given display and view"
+ ))
+@click.option(
+ "--config_path",
+ required=True,
+ help="path where to read ocio config file",
+ type=click.Path(exists=True))
+@click.option(
+ "--display",
+ required=True,
+ help="Display name",
+ type=click.STRING)
+@click.option(
+ "--view",
+ required=True,
+ help="view name",
+ type=click.STRING)
+@click.option(
+ "--output_path",
+ required=True,
+ help="path where to write output json file",
+ type=click.Path())
+def _get_display_view_colorspace_name(
+ config_path, display, view, output_path
+):
"""Aggregate view colorspace name to file.
Wrapper command for processes without access to OpenColorIO
Args:
- in_path (str): config file path string
- out_path (str): temp json file path string
+ config_path (str): config file path string
+ output_path (str): temp json file path string
display (str): display name e.g. "ACES"
view (str): view name e.g. "sRGB"
Example of use:
> pyton.exe ./ocio_wrapper.py config \
- get_display_view_colorspace_name --in_path= \
- --out_path= --display= --view=
+ get_display_view_colorspace_name --config_path \
+ --output_path --display --view
"""
+ _save_output_to_json_file(
+ get_display_view_colorspace_name(config_path, display, view),
+ output_path
+ )
- out_data = _get_display_view_colorspace_name(in_path,
- display,
- view)
- with open(out_path, "w") as f:
- json.dump(out_data, f)
-
- print(f"Display view colorspace saved to '{out_path}'")
-
-if __name__ == '__main__':
+if __name__ == "__main__":
+ if not has_compatible_ocio_package():
+ raise RuntimeError("OpenColorIO is not available.")
main()
diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py
index 7d97d7d66d..26bf638c91 100644
--- a/client/ayon_core/tools/adobe_webserver/app.py
+++ b/client/ayon_core/tools/adobe_webserver/app.py
@@ -104,14 +104,11 @@ class WebServerTool:
again. In that case, use existing running webserver.
Check here is easier than capturing exception from thread.
"""
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- result = True
- try:
- sock.bind((host_name, port))
- result = False
- except:
- print("Port is in use")
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as con:
+ result = con.connect_ex((host_name, port)) == 0
+ if result:
+ print(f"Port {port} is already in use")
return result
def call(self, func):
diff --git a/client/ayon_core/tools/common_models/__init__.py b/client/ayon_core/tools/common_models/__init__.py
index 8895515b1a..f09edfeab2 100644
--- a/client/ayon_core/tools/common_models/__init__.py
+++ b/client/ayon_core/tools/common_models/__init__.py
@@ -14,6 +14,7 @@ from .hierarchy import (
)
from .thumbnails import ThumbnailsModel
from .selection import HierarchyExpectedSelection
+from .users import UsersModel
__all__ = (
@@ -32,4 +33,6 @@ __all__ = (
"ThumbnailsModel",
"HierarchyExpectedSelection",
+
+ "UsersModel",
)
diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py
index 19a38bee21..89dd881a10 100644
--- a/client/ayon_core/tools/common_models/projects.py
+++ b/client/ayon_core/tools/common_models/projects.py
@@ -5,7 +5,7 @@ import ayon_api
import six
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"
@@ -17,6 +17,49 @@ class AbstractHierarchyController:
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:
"""Item representing folder entity on a server.
@@ -40,6 +83,23 @@ class ProjectItem:
}
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):
"""Converts folder item to data.
@@ -79,7 +139,7 @@ def _get_project_items_from_entitiy(projects):
"""
return [
- ProjectItem(project["name"], project["active"], project["library"])
+ ProjectItem.from_entity(project)
for project in projects
]
@@ -87,18 +147,29 @@ def _get_project_items_from_entitiy(projects):
class ProjectsModel(object):
def __init__(self, controller):
self._projects_cache = CacheItem(default_factory=list)
- self._project_items_by_name = {}
- self._projects_by_name = {}
+ self._project_statuses_cache = NestedCacheItem(
+ levels=1, default_factory=list
+ )
+ self._projects_by_name = NestedCacheItem(
+ levels=1, default_factory=list
+ )
self._is_refreshing = False
self._controller = controller
def reset(self):
self._projects_cache.reset()
- self._project_items_by_name = {}
- self._projects_by_name = {}
+ self._project_statuses_cache.reset()
+ self._projects_by_name.reset()
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()
def get_project_items(self, sender):
@@ -117,12 +188,51 @@ class ProjectsModel(object):
return self._projects_cache.get_data()
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
if project_name:
entity = ayon_api.get_project(project_name)
- self._projects_by_name[project_name] = entity
- return self._projects_by_name[project_name]
+ project_cache.update_data(entity)
+ 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
def _project_refresh_event_manager(self, sender):
@@ -143,6 +253,23 @@ class ProjectsModel(object):
)
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):
if self._is_refreshing:
return None
diff --git a/client/ayon_core/tools/common_models/thumbnails.py b/client/ayon_core/tools/common_models/thumbnails.py
index 6d14783b9a..2fa1e36e5c 100644
--- a/client/ayon_core/tools/common_models/thumbnails.py
+++ b/client/ayon_core/tools/common_models/thumbnails.py
@@ -1,234 +1,15 @@
-import os
-import time
import collections
import ayon_api
-import appdirs
from ayon_core.lib import NestedCacheItem
-
-FileInfo = collections.namedtuple(
- "FileInfo",
- ("path", "size", "modification_time")
-)
-
-
-class ThumbnailsCache:
- """Cache of thumbnails on local storage.
-
- Thumbnails are cached to appdirs to predefined directory. Each project has
- own subfolder with thumbnails -> that's because each project has own
- thumbnail id validation and file names are thumbnail ids with matching
- extension. Extensions are predefined (.png and .jpeg).
-
- Cache has cleanup mechanism which is triggered on initialized by default.
-
- The cleanup has 2 levels:
- 1. soft cleanup which remove all files that are older then 'days_alive'
- 2. max size cleanup which remove all files until the thumbnails folder
- contains less then 'max_filesize'
- - this is time consuming so it's not triggered automatically
-
- Args:
- cleanup (bool): Trigger soft cleanup (Cleanup expired thumbnails).
- """
-
- # Lifetime of thumbnails (in seconds)
- # - default 3 days
- days_alive = 3
- # Max size of thumbnail directory (in bytes)
- # - default 2 Gb
- max_filesize = 2 * 1024 * 1024 * 1024
-
- def __init__(self, cleanup=True):
- self._thumbnails_dir = None
- self._days_alive_secs = self.days_alive * 24 * 60 * 60
- if cleanup:
- self.cleanup()
-
- def get_thumbnails_dir(self):
- """Root directory where thumbnails are stored.
-
- Returns:
- str: Path to thumbnails root.
- """
-
- if self._thumbnails_dir is None:
- # TODO use generic function
- directory = appdirs.user_data_dir("AYON", "Ynput")
- self._thumbnails_dir = os.path.join(directory, "thumbnails")
- return self._thumbnails_dir
-
- thumbnails_dir = property(get_thumbnails_dir)
-
- def get_thumbnails_dir_file_info(self):
- """Get information about all files in thumbnails directory.
-
- Returns:
- List[FileInfo]: List of file information about all files.
- """
-
- thumbnails_dir = self.thumbnails_dir
- files_info = []
- if not os.path.exists(thumbnails_dir):
- return files_info
-
- for root, _, filenames in os.walk(thumbnails_dir):
- for filename in filenames:
- path = os.path.join(root, filename)
- files_info.append(FileInfo(
- path, os.path.getsize(path), os.path.getmtime(path)
- ))
- return files_info
-
- def get_thumbnails_dir_size(self, files_info=None):
- """Got full size of thumbnail directory.
-
- Args:
- files_info (List[FileInfo]): Prepared file information about
- files in thumbnail directory.
-
- Returns:
- int: File size of all files in thumbnail directory.
- """
-
- if files_info is None:
- files_info = self.get_thumbnails_dir_file_info()
-
- if not files_info:
- return 0
-
- return sum(
- file_info.size
- for file_info in files_info
- )
-
- def cleanup(self, check_max_size=False):
- """Cleanup thumbnails directory.
-
- Args:
- check_max_size (bool): Also cleanup files to match max size of
- thumbnails directory.
- """
-
- thumbnails_dir = self.get_thumbnails_dir()
- # Skip if thumbnails dir does not exist yet
- if not os.path.exists(thumbnails_dir):
- return
-
- self._soft_cleanup(thumbnails_dir)
- if check_max_size:
- self._max_size_cleanup(thumbnails_dir)
-
- def _soft_cleanup(self, thumbnails_dir):
- current_time = time.time()
- for root, _, filenames in os.walk(thumbnails_dir):
- for filename in filenames:
- path = os.path.join(root, filename)
- modification_time = os.path.getmtime(path)
- if current_time - modification_time > self._days_alive_secs:
- os.remove(path)
-
- def _max_size_cleanup(self, thumbnails_dir):
- files_info = self.get_thumbnails_dir_file_info()
- size = self.get_thumbnails_dir_size(files_info)
- if size < self.max_filesize:
- return
-
- sorted_file_info = collections.deque(
- sorted(files_info, key=lambda item: item.modification_time)
- )
- diff = size - self.max_filesize
- while diff > 0:
- if not sorted_file_info:
- break
-
- file_info = sorted_file_info.popleft()
- diff -= file_info.size
- os.remove(file_info.path)
-
- def get_thumbnail_filepath(self, project_name, thumbnail_id):
- """Get thumbnail by thumbnail id.
-
- Args:
- project_name (str): Name of project.
- thumbnail_id (str): Thumbnail id.
-
- Returns:
- Union[str, None]: Path to thumbnail image or None if thumbnail
- is not cached yet.
- """
-
- if not thumbnail_id:
- return None
-
- for ext in (
- ".png",
- ".jpeg",
- ):
- filepath = os.path.join(
- self.thumbnails_dir, project_name, thumbnail_id + ext
- )
- if os.path.exists(filepath):
- return filepath
- return None
-
- def get_project_dir(self, project_name):
- """Path to root directory for specific project.
-
- Args:
- project_name (str): Name of project for which root directory path
- should be returned.
-
- Returns:
- str: Path to root of project's thumbnails.
- """
-
- return os.path.join(self.thumbnails_dir, project_name)
-
- def make_sure_project_dir_exists(self, project_name):
- project_dir = self.get_project_dir(project_name)
- if not os.path.exists(project_dir):
- os.makedirs(project_dir)
- return project_dir
-
- def store_thumbnail(self, project_name, thumbnail_id, content, mime_type):
- """Store thumbnail to cache folder.
-
- Args:
- project_name (str): Project where the thumbnail belong to.
- thumbnail_id (str): Id of thumbnail.
- content (bytes): Byte content of thumbnail file.
- mime_data (str): Type of content.
-
- Returns:
- str: Path to cached thumbnail image file.
- """
-
- if mime_type == "image/png":
- ext = ".png"
- elif mime_type == "image/jpeg":
- ext = ".jpeg"
- else:
- raise ValueError(
- "Unknown mime type for thumbnail \"{}\"".format(mime_type))
-
- project_dir = self.make_sure_project_dir_exists(project_name)
- thumbnail_path = os.path.join(project_dir, thumbnail_id + ext)
- with open(thumbnail_path, "wb") as stream:
- stream.write(content)
-
- current_time = time.time()
- os.utime(thumbnail_path, (current_time, current_time))
-
- return thumbnail_path
+from ayon_core.pipeline.thumbnails import get_thumbnail_path
class ThumbnailsModel:
entity_cache_lifetime = 240 # In seconds
def __init__(self):
- self._thumbnail_cache = ThumbnailsCache()
self._paths_cache = collections.defaultdict(dict)
self._folders_cache = NestedCacheItem(
levels=2, lifetime=self.entity_cache_lifetime)
@@ -283,28 +64,7 @@ class ThumbnailsModel:
if thumbnail_id in project_cache:
return project_cache[thumbnail_id]
- filepath = self._thumbnail_cache.get_thumbnail_filepath(
- project_name, thumbnail_id
- )
- if filepath is not None:
- project_cache[thumbnail_id] = filepath
- return filepath
-
- # 'ayon_api' had a bug, public function
- # 'get_thumbnail_by_id' did not return output of
- # 'ServerAPI' method.
- con = ayon_api.get_server_api_connection()
- result = con.get_thumbnail_by_id(project_name, thumbnail_id)
- if result is None:
- pass
-
- elif result.is_valid:
- filepath = self._thumbnail_cache.store_thumbnail(
- project_name,
- thumbnail_id,
- result.content,
- result.content_type
- )
+ filepath = get_thumbnail_path(project_name, thumbnail_id)
project_cache[thumbnail_id] = filepath
return filepath
diff --git a/client/ayon_core/tools/common_models/users.py b/client/ayon_core/tools/common_models/users.py
new file mode 100644
index 0000000000..f8beb31aa1
--- /dev/null
+++ b/client/ayon_core/tools/common_models/users.py
@@ -0,0 +1,84 @@
+import ayon_api
+
+from ayon_core.lib import CacheItem
+
+
+class UserItem:
+ def __init__(
+ self,
+ username,
+ full_name,
+ email,
+ avatar_url,
+ active,
+ ):
+ self.username = username
+ self.full_name = full_name
+ self.email = email
+ self.avatar_url = avatar_url
+ self.active = active
+
+ @classmethod
+ def from_entity_data(cls, user_data):
+ return cls(
+ user_data["name"],
+ user_data["attrib"]["fullName"],
+ user_data["attrib"]["email"],
+ user_data["attrib"]["avatarUrl"],
+ user_data["active"],
+ )
+
+
+class UsersModel:
+ def __init__(self, controller):
+ self._controller = controller
+ self._users_cache = CacheItem(default_factory=list)
+
+ def get_user_items(self):
+ """Get user items.
+
+ Returns:
+ List[UserItem]: List of user items.
+
+ """
+ self._invalidate_cache()
+ return self._users_cache.get_data()
+
+ def get_user_items_by_name(self):
+ """Get user items by name.
+
+ Implemented as most of cases using this model will need to find
+ user information by username.
+
+ Returns:
+ Dict[str, UserItem]: Dictionary of user items by name.
+
+ """
+ return {
+ user_item.username: user_item
+ for user_item in self.get_user_items()
+ }
+
+ def get_user_item_by_username(self, username):
+ """Get user item by username.
+
+ Args:
+ username (str): Username.
+
+ Returns:
+ Union[UserItem, None]: User item or None if not found.
+
+ """
+ self._invalidate_cache()
+ for user_item in self.get_user_items():
+ if user_item.username == username:
+ return user_item
+ return None
+
+ def _invalidate_cache(self):
+ if self._users_cache.is_valid:
+ return
+ self._users_cache.update_data([
+ UserItem.from_entity_data(user)
+ for user in ayon_api.get_users()
+ ])
diff --git a/client/ayon_core/tools/launcher/ui/actions_widget.py b/client/ayon_core/tools/launcher/ui/actions_widget.py
index a225827418..2ffce13292 100644
--- a/client/ayon_core/tools/launcher/ui/actions_widget.py
+++ b/client/ayon_core/tools/launcher/ui/actions_widget.py
@@ -290,6 +290,34 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate):
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):
def __init__(self, controller, parent):
super(ActionsWidget, self).__init__(parent)
@@ -316,10 +344,7 @@ class ActionsWidget(QtWidgets.QWidget):
model = ActionsQtModel(controller)
- proxy_model = QtCore.QSortFilterProxyModel()
- proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
- proxy_model.setSortRole(ACTION_SORT_ROLE)
-
+ proxy_model = ActionsProxyModel()
proxy_model.setSourceModel(model)
view.setModel(proxy_model)
@@ -359,7 +384,8 @@ class ActionsWidget(QtWidgets.QWidget):
def _on_model_refresh(self):
self._proxy_model.sort(0)
# Force repaint all items
- self._view.update()
+ viewport = self._view.viewport()
+ viewport.update()
def _on_animation(self):
time_now = time.time()
diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py
index 7a7d335092..509db4d037 100644
--- a/client/ayon_core/tools/loader/abstract.py
+++ b/client/ayon_core/tools/loader/abstract.py
@@ -114,6 +114,7 @@ class VersionItem:
thumbnail_id (Union[str, None]): Thumbnail id.
published_time (Union[str, None]): Published time in format
'%Y%m%dT%H%M%SZ'.
+ status (Union[str, None]): Status name.
author (Union[str, None]): Author.
frame_range (Union[str, None]): Frame range.
duration (Union[int, None]): Duration.
@@ -132,6 +133,7 @@ class VersionItem:
thumbnail_id,
published_time,
author,
+ status,
frame_range,
duration,
handles,
@@ -146,6 +148,7 @@ class VersionItem:
self.is_hero = is_hero
self.published_time = published_time
self.author = author
+ self.status = status
self.frame_range = frame_range
self.duration = duration
self.handles = handles
@@ -185,6 +188,7 @@ class VersionItem:
"is_hero": self.is_hero,
"published_time": self.published_time,
"author": self.author,
+ "status": self.status,
"frame_range": self.frame_range,
"duration": self.duration,
"handles": self.handles,
@@ -488,6 +492,27 @@ class FrontendLoaderController(_BaseLoaderController):
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
def get_product_items(self, project_name, folder_ids, sender=None):
"""Product items for folder ids.
diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py
index 0c9bb369c7..35188369c2 100644
--- a/client/ayon_core/tools/loader/control.py
+++ b/client/ayon_core/tools/loader/control.py
@@ -180,6 +180,11 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
def get_project_items(self, sender=None):
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):
return self._hierarchy_model.get_folder_items(project_name, sender)
diff --git a/client/ayon_core/tools/loader/models/products.py b/client/ayon_core/tools/loader/models/products.py
index a3bbc30a09..c9325c4480 100644
--- a/client/ayon_core/tools/loader/models/products.py
+++ b/client/ayon_core/tools/loader/models/products.py
@@ -58,6 +58,7 @@ def version_item_from_entity(version):
thumbnail_id=version["thumbnailId"],
published_time=published_time,
author=author,
+ status=version["status"],
frame_range=frame_range,
duration=duration,
handles=handles,
@@ -526,8 +527,11 @@ class ProductsModel:
products = list(ayon_api.get_products(project_name, **kwargs))
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(
- project_name, product_ids=product_ids
+ project_name, product_ids=product_ids, fields=fields
)
return self._create_product_items(
diff --git a/client/ayon_core/tools/loader/ui/products_delegates.py b/client/ayon_core/tools/loader/ui/products_delegates.py
index 12ed1165ae..cedac6199b 100644
--- a/client/ayon_core/tools/loader/ui/products_delegates.py
+++ b/client/ayon_core/tools/loader/ui/products_delegates.py
@@ -104,7 +104,10 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate):
style = QtWidgets.QApplication.style()
style.drawControl(
- style.CE_ItemViewItem, option, painter, option.widget
+ QtWidgets.QCommonStyle.CE_ItemViewItem,
+ option,
+ painter,
+ option.widget
)
painter.save()
@@ -116,9 +119,14 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate):
pen.setColor(fg_color)
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(
- style.PM_FocusFrameHMargin, option, option.widget
+ QtWidgets.QCommonStyle.PM_FocusFrameHMargin,
+ option,
+ option.widget
) + 1
painter.drawText(
diff --git a/client/ayon_core/tools/loader/ui/products_model.py b/client/ayon_core/tools/loader/ui/products_model.py
index b465679c3b..8035b1f0fe 100644
--- a/client/ayon_core/tools/loader/ui/products_model.py
+++ b/client/ayon_core/tools/loader/ui/products_model.py
@@ -22,18 +22,22 @@ VERSION_HERO_ROLE = QtCore.Qt.UserRole + 11
VERSION_NAME_ROLE = QtCore.Qt.UserRole + 12
VERSION_NAME_EDIT_ROLE = QtCore.Qt.UserRole + 13
VERSION_PUBLISH_TIME_ROLE = QtCore.Qt.UserRole + 14
-VERSION_AUTHOR_ROLE = QtCore.Qt.UserRole + 15
-VERSION_FRAME_RANGE_ROLE = QtCore.Qt.UserRole + 16
-VERSION_DURATION_ROLE = QtCore.Qt.UserRole + 17
-VERSION_HANDLES_ROLE = QtCore.Qt.UserRole + 18
-VERSION_STEP_ROLE = QtCore.Qt.UserRole + 19
-VERSION_AVAILABLE_ROLE = QtCore.Qt.UserRole + 20
-VERSION_THUMBNAIL_ID_ROLE = QtCore.Qt.UserRole + 21
-ACTIVE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 22
-REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 23
-REPRESENTATIONS_COUNT_ROLE = QtCore.Qt.UserRole + 24
-SYNC_ACTIVE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 25
-SYNC_REMOTE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 26
+VERSION_STATUS_NAME_ROLE = QtCore.Qt.UserRole + 15
+VERSION_STATUS_SHORT_ROLE = QtCore.Qt.UserRole + 16
+VERSION_STATUS_COLOR_ROLE = QtCore.Qt.UserRole + 17
+VERSION_STATUS_ICON_ROLE = QtCore.Qt.UserRole + 18
+VERSION_AUTHOR_ROLE = QtCore.Qt.UserRole + 19
+VERSION_FRAME_RANGE_ROLE = QtCore.Qt.UserRole + 20
+VERSION_DURATION_ROLE = QtCore.Qt.UserRole + 21
+VERSION_HANDLES_ROLE = QtCore.Qt.UserRole + 22
+VERSION_STEP_ROLE = QtCore.Qt.UserRole + 23
+VERSION_AVAILABLE_ROLE = QtCore.Qt.UserRole + 24
+VERSION_THUMBNAIL_ID_ROLE = QtCore.Qt.UserRole + 25
+ACTIVE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 26
+REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 27
+REPRESENTATIONS_COUNT_ROLE = QtCore.Qt.UserRole + 28
+SYNC_ACTIVE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 29
+SYNC_REMOTE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 30
class ProductsModel(QtGui.QStandardItemModel):
@@ -44,6 +48,7 @@ class ProductsModel(QtGui.QStandardItemModel):
"Product type",
"Folder",
"Version",
+ "Status",
"Time",
"Author",
"Frames",
@@ -69,11 +74,35 @@ class ProductsModel(QtGui.QStandardItemModel):
]
]
- version_col = column_labels.index("Version")
- published_time_col = column_labels.index("Time")
+ product_name_col = column_labels.index("Product name")
+ product_type_col = column_labels.index("Product type")
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")
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):
super(ProductsModel, self).__init__()
@@ -96,6 +125,7 @@ class ProductsModel(QtGui.QStandardItemModel):
self._last_project_name = None
self._last_folder_ids = []
+ self._last_project_statuses = {}
def get_product_item_indexes(self):
return [
@@ -141,6 +171,15 @@ class ProductsModel(QtGui.QStandardItemModel):
if not index.isValid():
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()
if col == 0:
return super(ProductsModel, self).data(index, role)
@@ -168,29 +207,8 @@ class ProductsModel(QtGui.QStandardItemModel):
if role == QtCore.Qt.DisplayRole:
if not index.data(PRODUCT_ID_ROLE):
return None
- if col == self.version_col:
- role = VERSION_NAME_ROLE
- 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:
+ role = self._display_role_mapping.get(col)
+ if role is None:
return None
index = self.index(index.row(), 0, index.parent())
@@ -312,6 +330,7 @@ class ProductsModel(QtGui.QStandardItemModel):
version_item.published_time, VERSION_PUBLISH_TIME_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.duration, VERSION_DURATION_ROLE)
model_item.setData(version_item.handles, VERSION_HANDLES_ROLE)
@@ -393,6 +412,11 @@ class ProductsModel(QtGui.QStandardItemModel):
self._last_project_name = project_name
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(
project_name
diff --git a/client/ayon_core/tools/loader/ui/products_widget.py b/client/ayon_core/tools/loader/ui/products_widget.py
index d9f027153e..61ddd690e9 100644
--- a/client/ayon_core/tools/loader/ui/products_widget.py
+++ b/client/ayon_core/tools/loader/ui/products_widget.py
@@ -6,7 +6,7 @@ from ayon_core.tools.utils import (
RecursiveSortFilterProxyModel,
DeselectableTreeView,
)
-from ayon_core.tools.utils.delegates import PrettyTimeDelegate
+from ayon_core.tools.utils.delegates import PrettyTimeDelegate, StatusDelegate
from .products_model import (
ProductsModel,
@@ -17,12 +17,16 @@ from .products_model import (
FOLDER_ID_ROLE,
PRODUCT_ID_ROLE,
VERSION_ID_ROLE,
+ VERSION_STATUS_NAME_ROLE,
+ VERSION_STATUS_SHORT_ROLE,
+ VERSION_STATUS_COLOR_ROLE,
+ VERSION_STATUS_ICON_ROLE,
VERSION_THUMBNAIL_ID_ROLE,
)
from .products_delegates import (
VersionDelegate,
LoadedInSceneDelegate,
- SiteSyncDelegate
+ SiteSyncDelegate,
)
from .actions_utils import show_actions_menu
@@ -89,6 +93,7 @@ class ProductsWidget(QtWidgets.QWidget):
90, # Product type
130, # Folder label
60, # Version
+ 100, # Status
125, # Time
75, # Author
75, # Frames
@@ -128,20 +133,24 @@ class ProductsWidget(QtWidgets.QWidget):
products_view.setColumnWidth(idx, width)
version_delegate = VersionDelegate()
- products_view.setItemDelegateForColumn(
- products_model.version_col, version_delegate)
-
time_delegate = PrettyTimeDelegate()
- products_view.setItemDelegateForColumn(
- products_model.published_time_col, time_delegate)
-
+ status_delegate = StatusDelegate(
+ VERSION_STATUS_NAME_ROLE,
+ VERSION_STATUS_SHORT_ROLE,
+ VERSION_STATUS_COLOR_ROLE,
+ VERSION_STATUS_ICON_ROLE,
+ )
in_scene_delegate = LoadedInSceneDelegate()
- products_view.setItemDelegateForColumn(
- products_model.in_scene_col, in_scene_delegate)
-
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.setContentsMargins(0, 0, 0, 0)
@@ -175,6 +184,7 @@ class ProductsWidget(QtWidgets.QWidget):
self._version_delegate = version_delegate
self._time_delegate = time_delegate
+ self._status_delegate = status_delegate
self._in_scene_delegate = in_scene_delegate
self._sitesync_delegate = sitesync_delegate
diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py
index 3a6f4679fa..8529a53b06 100644
--- a/client/ayon_core/tools/loader/ui/window.py
+++ b/client/ayon_core/tools/loader/ui/window.py
@@ -335,9 +335,7 @@ class LoaderWindow(QtWidgets.QWidget):
def closeEvent(self, event):
super(LoaderWindow, self).closeEvent(event)
- # Deselect project so current context will be selected
- # on next 'showEvent'
- self._controller.set_selected_project(None)
+
self._reset_on_show = True
def keyPressEvent(self, event):
diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py
index 47c5399cf7..4e34f9b58c 100644
--- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py
+++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py
@@ -52,6 +52,7 @@ class SelectionTypes:
class BaseGroupWidget(QtWidgets.QWidget):
selected = QtCore.Signal(str, str, str)
removed_selected = QtCore.Signal()
+ double_clicked = QtCore.Signal()
def __init__(self, group_name, parent):
super(BaseGroupWidget, self).__init__(parent)
@@ -192,6 +193,7 @@ class ConvertorItemsGroupWidget(BaseGroupWidget):
else:
widget = ConvertorItemCardWidget(item, self)
widget.selected.connect(self._on_widget_selection)
+ widget.double_clicked(self.double_clicked)
self._widgets_by_id[item.id] = widget
self._content_layout.insertWidget(widget_idx, widget)
widget_idx += 1
@@ -254,6 +256,7 @@ class InstanceGroupWidget(BaseGroupWidget):
)
widget.selected.connect(self._on_widget_selection)
widget.active_changed.connect(self._on_active_changed)
+ widget.double_clicked.connect(self.double_clicked)
self._widgets_by_id[instance.id] = widget
self._content_layout.insertWidget(widget_idx, widget)
widget_idx += 1
@@ -271,6 +274,7 @@ class CardWidget(BaseClickableFrame):
# Group identifier of card
# - this must be set because if send when mouse is released with card id
_group_identifier = None
+ double_clicked = QtCore.Signal()
def __init__(self, parent):
super(CardWidget, self).__init__(parent)
@@ -279,6 +283,11 @@ class CardWidget(BaseClickableFrame):
self._selected = False
self._id = None
+ def mouseDoubleClickEvent(self, event):
+ super(CardWidget, self).mouseDoubleClickEvent(event)
+ if self._is_valid_double_click(event):
+ self.double_clicked.emit()
+
@property
def id(self):
"""Id of card."""
@@ -312,6 +321,9 @@ class CardWidget(BaseClickableFrame):
self.selected.emit(self._id, self._group_identifier, selection_type)
+ def _is_valid_double_click(self, event):
+ return True
+
class ContextCardWidget(CardWidget):
"""Card for global context.
@@ -527,6 +539,15 @@ class InstanceCardWidget(CardWidget):
def _on_expend_clicked(self):
self._set_expanded()
+ def _is_valid_double_click(self, event):
+ widget = self.childAt(event.pos())
+ if (
+ widget is self._active_checkbox
+ or widget is self._expand_btn
+ ):
+ return False
+ return True
+
class InstanceCardView(AbstractInstanceView):
"""Publish access to card view.
@@ -534,6 +555,8 @@ class InstanceCardView(AbstractInstanceView):
Wrapper of all widgets in card view.
"""
+ double_clicked = QtCore.Signal()
+
def __init__(self, controller, parent):
super(InstanceCardView, self).__init__(parent)
@@ -715,6 +738,7 @@ class InstanceCardView(AbstractInstanceView):
)
group_widget.active_changed.connect(self._on_active_changed)
group_widget.selected.connect(self._on_widget_selection)
+ group_widget.double_clicked.connect(self.double_clicked)
self._content_layout.insertWidget(widget_idx, group_widget)
self._widgets_by_group[group_name] = group_widget
@@ -755,6 +779,7 @@ class InstanceCardView(AbstractInstanceView):
widget = ContextCardWidget(self._content_widget)
widget.selected.connect(self._on_widget_selection)
+ widget.double_clicked.connect(self.double_clicked)
self._context_widget = widget
@@ -778,6 +803,7 @@ class InstanceCardView(AbstractInstanceView):
CONVERTOR_ITEM_GROUP, self._content_widget
)
group_widget.selected.connect(self._on_widget_selection)
+ group_widget.double_clicked.connect(self.double_clicked)
self._content_layout.insertWidget(1, group_widget)
self._convertor_items_group = group_widget
diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py
index 3322a73be6..71be0ab1a4 100644
--- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py
+++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py
@@ -110,6 +110,7 @@ class InstanceListItemWidget(QtWidgets.QWidget):
This is required to be able use custom checkbox on custom place.
"""
active_changed = QtCore.Signal(str, bool)
+ double_clicked = QtCore.Signal()
def __init__(self, instance, parent):
super(InstanceListItemWidget, self).__init__(parent)
@@ -149,6 +150,12 @@ class InstanceListItemWidget(QtWidgets.QWidget):
self._set_valid_property(instance.has_valid_context)
+ def mouseDoubleClickEvent(self, event):
+ widget = self.childAt(event.pos())
+ super(InstanceListItemWidget, self).mouseDoubleClickEvent(event)
+ if widget is not self._active_checkbox:
+ self.double_clicked.emit()
+
def _set_valid_property(self, valid):
if self._has_valid_context == valid:
return
@@ -209,6 +216,8 @@ class InstanceListItemWidget(QtWidgets.QWidget):
class ListContextWidget(QtWidgets.QFrame):
"""Context (or global attributes) widget."""
+ double_clicked = QtCore.Signal()
+
def __init__(self, parent):
super(ListContextWidget, self).__init__(parent)
@@ -225,6 +234,10 @@ class ListContextWidget(QtWidgets.QFrame):
self.label_widget = label_widget
+ def mouseDoubleClickEvent(self, event):
+ super(ListContextWidget, self).mouseDoubleClickEvent(event)
+ self.double_clicked.emit()
+
class InstanceListGroupWidget(QtWidgets.QFrame):
"""Widget representing group of instances.
@@ -317,6 +330,7 @@ class InstanceListGroupWidget(QtWidgets.QFrame):
class InstanceTreeView(QtWidgets.QTreeView):
"""View showing instances and their groups."""
toggle_requested = QtCore.Signal(int)
+ double_clicked = QtCore.Signal()
def __init__(self, *args, **kwargs):
super(InstanceTreeView, self).__init__(*args, **kwargs)
@@ -425,6 +439,9 @@ class InstanceListView(AbstractInstanceView):
This is public access to and from list view.
"""
+
+ double_clicked = QtCore.Signal()
+
def __init__(self, controller, parent):
super(InstanceListView, self).__init__(parent)
@@ -454,6 +471,7 @@ class InstanceListView(AbstractInstanceView):
instance_view.collapsed.connect(self._on_collapse)
instance_view.expanded.connect(self._on_expand)
instance_view.toggle_requested.connect(self._on_toggle_request)
+ instance_view.double_clicked.connect(self.double_clicked)
self._group_items = {}
self._group_widgets = {}
@@ -687,6 +705,7 @@ class InstanceListView(AbstractInstanceView):
self._active_toggle_enabled
)
widget.active_changed.connect(self._on_active_changed)
+ widget.double_clicked.connect(self.double_clicked)
self._instance_view.setIndexWidget(proxy_index, widget)
self._widgets_by_id[instance.id] = widget
@@ -717,6 +736,7 @@ class InstanceListView(AbstractInstanceView):
)
proxy_index = self._proxy_model.mapFromSource(index)
widget = ListContextWidget(self._instance_view)
+ widget.double_clicked.connect(self.double_clicked)
self._instance_view.setIndexWidget(proxy_index, widget)
self._context_widget = widget
diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py
index dd82185830..cedf52ae01 100644
--- a/client/ayon_core/tools/publisher/widgets/overview_widget.py
+++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py
@@ -18,6 +18,7 @@ class OverviewWidget(QtWidgets.QFrame):
instance_context_changed = QtCore.Signal()
create_requested = QtCore.Signal()
convert_requested = QtCore.Signal()
+ publish_tab_requested = QtCore.Signal()
anim_end_value = 200
anim_duration = 200
@@ -113,9 +114,15 @@ class OverviewWidget(QtWidgets.QFrame):
product_list_view.selection_changed.connect(
self._on_product_change
)
+ product_list_view.double_clicked.connect(
+ self.publish_tab_requested
+ )
product_view_cards.selection_changed.connect(
self._on_product_change
)
+ product_view_cards.double_clicked.connect(
+ self.publish_tab_requested
+ )
# Active instances changed
product_list_view.active_changed.connect(
self._on_active_changed
diff --git a/client/ayon_core/tools/publisher/window.py b/client/ayon_core/tools/publisher/window.py
index 123864ff6c..1b13ced317 100644
--- a/client/ayon_core/tools/publisher/window.py
+++ b/client/ayon_core/tools/publisher/window.py
@@ -258,6 +258,9 @@ class PublisherWindow(QtWidgets.QDialog):
overview_widget.convert_requested.connect(
self._on_convert_requested
)
+ overview_widget.publish_tab_requested.connect(
+ self._go_to_publish_tab
+ )
save_btn.clicked.connect(self._on_save_clicked)
reset_btn.clicked.connect(self._on_reset_clicked)
diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py
index 6e43050c05..5937ffa4da 100644
--- a/client/ayon_core/tools/push_to_project/models/integrate.py
+++ b/client/ayon_core/tools/push_to_project/models/integrate.py
@@ -723,7 +723,6 @@ class ProjectPushItemProcess:
dst_project_name = self._item.dst_project_name
dst_folder_id = self._item.dst_folder_id
dst_task_name = self._item.dst_task_name
- dst_task_name_low = dst_task_name.lower()
new_folder_name = self._item.new_folder_name
if not dst_folder_id and not new_folder_name:
self._status.set_failed(
@@ -765,7 +764,7 @@ class ProjectPushItemProcess:
dst_project_name, folder_ids=[folder_entity["id"]]
)
}
- task_info = folder_tasks.get(dst_task_name_low)
+ task_info = folder_tasks.get(dst_task_name.lower())
if not task_info:
self._status.set_failed(
f"Could find task with name \"{dst_task_name}\""
diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py
index 592113455c..b890462506 100644
--- a/client/ayon_core/tools/sceneinventory/control.py
+++ b/client/ayon_core/tools/sceneinventory/control.py
@@ -1,14 +1,14 @@
import ayon_api
from ayon_core.lib.events import QueuedEventSystem
-from ayon_core.host import ILoadHost
+from ayon_core.host import HostBase
from ayon_core.pipeline import (
registered_host,
get_current_context,
)
-from ayon_core.tools.common_models import HierarchyModel
+from ayon_core.tools.common_models import HierarchyModel, ProjectsModel
-from .models import SiteSyncModel
+from .models import SiteSyncModel, ContainersModel
class SceneInventoryController:
@@ -28,11 +28,16 @@ class SceneInventoryController:
self._current_folder_id = None
self._current_folder_set = False
+ self._containers_model = ContainersModel(self)
self._sitesync_model = SiteSyncModel(self)
# Switch dialog requirements
self._hierarchy_model = HierarchyModel(self)
+ self._projects_model = ProjectsModel(self)
self._event_system = self._create_event_system()
+ def get_host(self) -> HostBase:
+ return self._host
+
def emit_event(self, topic, data=None, source=None):
if data is None:
data = {}
@@ -47,6 +52,7 @@ class SceneInventoryController:
self._current_folder_id = None
self._current_folder_set = False
+ self._containers_model.reset()
self._sitesync_model.reset()
self._hierarchy_model.reset()
@@ -80,13 +86,32 @@ class SceneInventoryController:
self._current_folder_set = True
return self._current_folder_id
+ def get_project_status_items(self):
+ project_name = self.get_current_project_name()
+ return self._projects_model.get_project_status_items(
+ project_name, None
+ )
+
+ # Containers methods
def get_containers(self):
- host = self._host
- if isinstance(host, ILoadHost):
- return list(host.get_containers())
- elif hasattr(host, "ls"):
- return list(host.ls())
- return []
+ return self._containers_model.get_containers()
+
+ def get_containers_by_item_ids(self, item_ids):
+ return self._containers_model.get_containers_by_item_ids(item_ids)
+
+ def get_container_items(self):
+ return self._containers_model.get_container_items()
+
+ def get_container_items_by_id(self, item_ids):
+ return self._containers_model.get_container_items_by_id(item_ids)
+
+ def get_representation_info_items(self, representation_ids):
+ return self._containers_model.get_representation_info_items(
+ representation_ids
+ )
+
+ def get_version_items(self, product_ids):
+ return self._containers_model.get_version_items(product_ids)
# Site Sync methods
def is_sitesync_enabled(self):
diff --git a/client/ayon_core/tools/sceneinventory/delegates.py b/client/ayon_core/tools/sceneinventory/delegates.py
index 2126fa1cbe..6f91587613 100644
--- a/client/ayon_core/tools/sceneinventory/delegates.py
+++ b/client/ayon_core/tools/sceneinventory/delegates.py
@@ -1,38 +1,10 @@
-import numbers
-
-import ayon_api
-
-from ayon_core.pipeline import HeroVersionType
-from ayon_core.tools.utils.models import TreeModel
-from ayon_core.tools.utils.lib import format_version
-
from qtpy import QtWidgets, QtCore, QtGui
+from .model import VERSION_LABEL_ROLE
+
class VersionDelegate(QtWidgets.QStyledItemDelegate):
"""A delegate that display version integer formatted as version string."""
-
- version_changed = QtCore.Signal()
- first_run = False
- lock = False
-
- def __init__(self, controller, *args, **kwargs):
- self._controller = controller
- super(VersionDelegate, self).__init__(*args, **kwargs)
-
- def get_project_name(self):
- return self._controller.get_current_project_name()
-
- def displayText(self, value, locale):
- if isinstance(value, HeroVersionType):
- return format_version(value)
- if not isinstance(value, numbers.Integral):
- # For cases where no version is resolved like NOT FOUND cases
- # where a representation might not exist in current database
- return
-
- return format_version(value)
-
def paint(self, painter, option, index):
fg_color = index.data(QtCore.Qt.ForegroundRole)
if fg_color:
@@ -44,7 +16,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate):
fg_color = None
if not fg_color:
- return super(VersionDelegate, self).paint(painter, option, index)
+ return super().paint(painter, option, index)
if option.widget:
style = option.widget.style()
@@ -60,9 +32,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate):
painter.save()
- text = self.displayText(
- index.data(QtCore.Qt.DisplayRole), option.locale
- )
+ text = index.data(VERSION_LABEL_ROLE)
pen = painter.pen()
pen.setColor(fg_color)
painter.setPen(pen)
@@ -82,77 +52,3 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate):
)
painter.restore()
-
- def createEditor(self, parent, option, index):
- item = index.data(TreeModel.ItemRole)
- if item.get("isGroup") or item.get("isMerged"):
- return
-
- editor = QtWidgets.QComboBox(parent)
-
- def commit_data():
- if not self.first_run:
- self.commitData.emit(editor) # Update model data
- self.version_changed.emit() # Display model data
- editor.currentIndexChanged.connect(commit_data)
-
- self.first_run = True
- self.lock = False
-
- return editor
-
- def setEditorData(self, editor, index):
- if self.lock:
- # Only set editor data once per delegation
- return
-
- editor.clear()
-
- # Current value of the index
- item = index.data(TreeModel.ItemRole)
- value = index.data(QtCore.Qt.DisplayRole)
-
- project_name = self.get_project_name()
- # Add all available versions to the editor
- product_id = item["version_entity"]["productId"]
- version_entities = list(sorted(
- ayon_api.get_versions(
- project_name, product_ids={product_id}, active=True
- ),
- key=lambda item: abs(item["version"])
- ))
-
- selected = None
- items = []
- is_hero_version = value < 0
- for version_entity in version_entities:
- version = version_entity["version"]
- label = format_version(version)
- item = QtGui.QStandardItem(label)
- item.setData(version_entity, QtCore.Qt.UserRole)
- items.append(item)
-
- if (
- version == value
- or is_hero_version and version < 0
- ):
- selected = item
-
- # Reverse items so latest versions be upper
- items.reverse()
- for item in items:
- editor.model().appendRow(item)
-
- index = 0
- if selected:
- index = selected.row()
-
- # Will trigger index-change signal
- editor.setCurrentIndex(index)
- self.first_run = False
- self.lock = True
-
- def setModelData(self, editor, model, index):
- """Apply the integer version back in the model"""
- version = editor.itemData(editor.currentIndex())
- model.setData(index, version["name"])
diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py
index 330b174218..1c51b7a98b 100644
--- a/client/ayon_core/tools/sceneinventory/model.py
+++ b/client/ayon_core/tools/sceneinventory/model.py
@@ -1,57 +1,113 @@
import re
import logging
-import uuid
-from collections import defaultdict
+import collections
-import ayon_api
from qtpy import QtCore, QtGui
import qtawesome
-from ayon_core.pipeline import (
- get_current_project_name,
- HeroVersionType,
-)
from ayon_core.style import get_default_entity_icon_color
from ayon_core.tools.utils import get_qt_icon
-from ayon_core.tools.utils.models import TreeModel, Item
+from ayon_core.tools.utils.lib import format_version
+
+ITEM_ID_ROLE = QtCore.Qt.UserRole + 1
+NAME_COLOR_ROLE = QtCore.Qt.UserRole + 2
+COUNT_ROLE = QtCore.Qt.UserRole + 3
+IS_CONTAINER_ITEM_ROLE = QtCore.Qt.UserRole + 4
+VERSION_IS_LATEST_ROLE = QtCore.Qt.UserRole + 5
+VERSION_IS_HERO_ROLE = QtCore.Qt.UserRole + 6
+VERSION_LABEL_ROLE = QtCore.Qt.UserRole + 7
+VERSION_COLOR_ROLE = QtCore.Qt.UserRole + 8
+STATUS_NAME_ROLE = QtCore.Qt.UserRole + 9
+STATUS_COLOR_ROLE = QtCore.Qt.UserRole + 10
+STATUS_SHORT_ROLE = QtCore.Qt.UserRole + 11
+STATUS_ICON_ROLE = QtCore.Qt.UserRole + 12
+PRODUCT_ID_ROLE = QtCore.Qt.UserRole + 13
+PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 14
+PRODUCT_TYPE_ICON_ROLE = QtCore.Qt.UserRole + 15
+PRODUCT_GROUP_NAME_ROLE = QtCore.Qt.UserRole + 16
+PRODUCT_GROUP_ICON_ROLE = QtCore.Qt.UserRole + 17
+LOADER_NAME_ROLE = QtCore.Qt.UserRole + 18
+OBJECT_NAME_ROLE = QtCore.Qt.UserRole + 19
+ACTIVE_SITE_PROGRESS_ROLE = QtCore.Qt.UserRole + 20
+REMOTE_SITE_PROGRESS_ROLE = QtCore.Qt.UserRole + 21
+ACTIVE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 22
+REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 23
+# This value hold unique value of container that should be used to identify
+# containers inbetween refresh.
+ITEM_UNIQUE_NAME_ROLE = QtCore.Qt.UserRole + 24
-def walk_hierarchy(node):
- """Recursively yield group node."""
- for child in node.children():
- if child.get("isGroupNode"):
- yield child
-
- for _child in walk_hierarchy(child):
- yield _child
-
-
-class InventoryModel(TreeModel):
+class InventoryModel(QtGui.QStandardItemModel):
"""The model for the inventory"""
- Columns = [
+ column_labels = [
"Name",
- "version",
- "count",
- "productType",
- "group",
- "loader",
- "objectName",
- "active_site",
- "remote_site",
+ "Version",
+ "Status",
+ "Count",
+ "Product type",
+ "Group",
+ "Loader",
+ "Object name",
+ "Active site",
+ "Remote site",
]
- active_site_col = Columns.index("active_site")
- remote_site_col = Columns.index("remote_site")
+ name_col = column_labels.index("Name")
+ version_col = column_labels.index("Version")
+ status_col = column_labels.index("Status")
+ count_col = column_labels.index("Count")
+ product_type_col = column_labels.index("Product type")
+ product_group_col = column_labels.index("Group")
+ loader_col = column_labels.index("Loader")
+ object_name_col = column_labels.index("Object name")
+ active_site_col = column_labels.index("Active site")
+ remote_site_col = column_labels.index("Remote site")
+ display_role_by_column = {
+ name_col: QtCore.Qt.DisplayRole,
+ version_col: VERSION_LABEL_ROLE,
+ status_col: STATUS_NAME_ROLE,
+ count_col: COUNT_ROLE,
+ product_type_col: PRODUCT_TYPE_ROLE,
+ product_group_col: PRODUCT_GROUP_NAME_ROLE,
+ loader_col: LOADER_NAME_ROLE,
+ object_name_col: OBJECT_NAME_ROLE,
+ active_site_col: ACTIVE_SITE_PROGRESS_ROLE,
+ remote_site_col: REMOTE_SITE_PROGRESS_ROLE,
+ }
+ decoration_role_by_column = {
+ name_col: QtCore.Qt.DecorationRole,
+ product_type_col: PRODUCT_TYPE_ICON_ROLE,
+ product_group_col: PRODUCT_GROUP_ICON_ROLE,
+ active_site_col: ACTIVE_SITE_ICON_ROLE,
+ remote_site_col: REMOTE_SITE_ICON_ROLE,
+ }
+ foreground_role_by_column = {
+ name_col: NAME_COLOR_ROLE,
+ version_col: VERSION_COLOR_ROLE,
+ status_col: STATUS_COLOR_ROLE
+ }
+ width_by_column = {
+ name_col: 250,
+ version_col: 55,
+ status_col: 100,
+ count_col: 55,
+ product_type_col: 150,
+ product_group_col: 120,
+ loader_col: 150,
+ }
OUTDATED_COLOR = QtGui.QColor(235, 30, 30)
CHILD_OUTDATED_COLOR = QtGui.QColor(200, 160, 30)
GRAYOUT_COLOR = QtGui.QColor(160, 160, 160)
- UniqueRole = QtCore.Qt.UserRole + 2 # unique label role
-
def __init__(self, controller, parent=None):
- super(InventoryModel, self).__init__(parent)
+ super().__init__(parent)
+
+ self.setColumnCount(len(self.column_labels))
+ for idx, label in enumerate(self.column_labels):
+ self.setHeaderData(idx, QtCore.Qt.Horizontal, label)
+
self.log = logging.getLogger(self.__class__.__name__)
self._controller = controller
@@ -60,103 +116,217 @@ class InventoryModel(TreeModel):
self._default_icon_color = get_default_entity_icon_color()
- site_icons = self._controller.get_site_provider_icons()
-
- self._site_icons = {
- provider: get_qt_icon(icon_def)
- for provider, icon_def in site_icons.items()
- }
-
def outdated(self, item):
return item.get("isOutdated", True)
+ def refresh(self, selected=None):
+ """Refresh the model"""
+ # for debugging or testing, injecting items from outside
+ container_items = self._controller.get_container_items()
+
+ self._clear_items()
+
+ items_by_repre_id = {}
+ for container_item in container_items:
+ # if (
+ # selected is not None
+ # and container_item.item_id not in selected
+ # ):
+ # continue
+ repre_id = container_item.representation_id
+ items = items_by_repre_id.setdefault(repre_id, [])
+ items.append(container_item)
+
+ repre_id = set(items_by_repre_id.keys())
+ repre_info_by_id = self._controller.get_representation_info_items(
+ repre_id
+ )
+ product_ids = {
+ repre_info.product_id
+ for repre_info in repre_info_by_id.values()
+ }
+ version_items_by_product_id = self._controller.get_version_items(
+ product_ids
+ )
+ # SiteSync addon information
+ progress_by_id = self._controller.get_representations_site_progress(
+ repre_id
+ )
+ sites_info = self._controller.get_sites_information()
+ site_icons = {
+ provider: get_qt_icon(icon_def)
+ for provider, icon_def in (
+ self._controller.get_site_provider_icons().items()
+ )
+ }
+ status_items_by_name = {
+ status_item.name: status_item
+ for status_item in self._controller.get_project_status_items()
+ }
+
+ group_item_icon = qtawesome.icon(
+ "fa.folder", color=self._default_icon_color
+ )
+ valid_item_icon = qtawesome.icon(
+ "fa.file-o", color=self._default_icon_color
+ )
+ invalid_item_icon = qtawesome.icon(
+ "fa.exclamation-circle", color=self._default_icon_color
+ )
+ group_icon = qtawesome.icon(
+ "fa.object-group", color=self._default_icon_color
+ )
+ product_type_icon = qtawesome.icon(
+ "fa.folder", color="#0091B2"
+ )
+ group_item_font = QtGui.QFont()
+ group_item_font.setBold(True)
+
+ active_site_icon = site_icons.get(sites_info["active_site_provider"])
+ remote_site_icon = site_icons.get(sites_info["remote_site_provider"])
+
+ root_item = self.invisibleRootItem()
+
+ group_items = []
+ for repre_id, container_items in items_by_repre_id.items():
+ repre_info = repre_info_by_id[repre_id]
+ version_label = "N/A"
+ version_color = None
+ is_latest = False
+ is_hero = False
+ status_name = None
+ status_color = None
+ status_short = None
+ if not repre_info.is_valid:
+ group_name = "< Entity N/A >"
+ item_icon = invalid_item_icon
+
+ else:
+ group_name = "{}_{}: ({})".format(
+ repre_info.folder_path.rsplit("/")[-1],
+ repre_info.product_name,
+ repre_info.representation_name
+ )
+ item_icon = valid_item_icon
+
+ version_items = (
+ version_items_by_product_id[repre_info.product_id]
+ )
+ version_item = version_items[repre_info.version_id]
+ version_label = format_version(version_item.version)
+ is_hero = version_item.version < 0
+ is_latest = version_item.is_latest
+ if not is_latest:
+ version_color = self.OUTDATED_COLOR
+ status_name = version_item.status
+ status_item = status_items_by_name.get(status_name)
+ if status_item:
+ status_short = status_item.short
+ status_color = status_item.color
+
+ container_model_items = []
+ for container_item in container_items:
+ unique_name = (
+ repre_info.representation_name
+ + container_item.object_name or ""
+ )
+
+ item = QtGui.QStandardItem()
+ item.setColumnCount(root_item.columnCount())
+ item.setData(container_item.namespace, QtCore.Qt.DisplayRole)
+ item.setData(self.GRAYOUT_COLOR, NAME_COLOR_ROLE)
+ item.setData(self.GRAYOUT_COLOR, VERSION_COLOR_ROLE)
+ item.setData(item_icon, QtCore.Qt.DecorationRole)
+ item.setData(repre_info.product_id, PRODUCT_ID_ROLE)
+ item.setData(container_item.item_id, ITEM_ID_ROLE)
+ item.setData(version_label, VERSION_LABEL_ROLE)
+ item.setData(container_item.loader_name, LOADER_NAME_ROLE)
+ item.setData(container_item.object_name, OBJECT_NAME_ROLE)
+ item.setData(True, IS_CONTAINER_ITEM_ROLE)
+ item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE)
+ container_model_items.append(item)
+
+ if not container_model_items:
+ continue
+
+ progress = progress_by_id[repre_id]
+ active_site_progress = "{}%".format(
+ max(progress["active_site"], 0) * 100
+ )
+ remote_site_progress = "{}%".format(
+ max(progress["remote_site"], 0) * 100
+ )
+
+ group_item = QtGui.QStandardItem()
+ group_item.setColumnCount(root_item.columnCount())
+ group_item.setData(group_name, QtCore.Qt.DisplayRole)
+ group_item.setData(group_name, ITEM_UNIQUE_NAME_ROLE)
+ group_item.setData(group_item_icon, QtCore.Qt.DecorationRole)
+ group_item.setData(group_item_font, QtCore.Qt.FontRole)
+ group_item.setData(repre_info.product_id, PRODUCT_ID_ROLE)
+ group_item.setData(repre_info.product_type, PRODUCT_TYPE_ROLE)
+ group_item.setData(product_type_icon, PRODUCT_TYPE_ICON_ROLE)
+ group_item.setData(is_latest, VERSION_IS_LATEST_ROLE)
+ group_item.setData(is_hero, VERSION_IS_HERO_ROLE)
+ group_item.setData(version_label, VERSION_LABEL_ROLE)
+ group_item.setData(len(container_items), COUNT_ROLE)
+ group_item.setData(status_name, STATUS_NAME_ROLE)
+ group_item.setData(status_short, STATUS_SHORT_ROLE)
+ group_item.setData(status_color, STATUS_COLOR_ROLE)
+
+ group_item.setData(
+ active_site_progress, ACTIVE_SITE_PROGRESS_ROLE
+ )
+ group_item.setData(
+ remote_site_progress, REMOTE_SITE_PROGRESS_ROLE
+ )
+ group_item.setData(active_site_icon, ACTIVE_SITE_ICON_ROLE)
+ group_item.setData(remote_site_icon, REMOTE_SITE_ICON_ROLE)
+ group_item.setData(False, IS_CONTAINER_ITEM_ROLE)
+
+ if version_color is not None:
+ group_item.setData(version_color, VERSION_COLOR_ROLE)
+
+ if repre_info.product_group:
+ group_item.setData(
+ repre_info.product_group, PRODUCT_GROUP_NAME_ROLE
+ )
+ group_item.setData(group_icon, PRODUCT_GROUP_ICON_ROLE)
+
+ group_item.appendRows(container_model_items)
+ group_items.append(group_item)
+
+ if group_items:
+ root_item.appendRows(group_items)
+
+ def flags(self, index):
+ return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
+
def data(self, index, role):
if not index.isValid():
return
- item = index.internalPointer()
+ col = index.column()
+ if role == QtCore.Qt.DisplayRole:
+ role = self.display_role_by_column.get(col)
+ if role is None:
+ print(col, role)
+ return None
- if role == QtCore.Qt.FontRole:
- # Make top-level entries bold
- if item.get("isGroupNode") or item.get("isNotSet"): # group-item
- font = QtGui.QFont()
- font.setBold(True)
- return font
+ elif role == QtCore.Qt.DecorationRole:
+ role = self.decoration_role_by_column.get(col)
+ if role is None:
+ return None
- if role == QtCore.Qt.ForegroundRole:
- # Set the text color to the OUTDATED_COLOR when the
- # collected version is not the same as the highest version
- key = self.Columns[index.column()]
- if key == "version": # version
- if item.get("isGroupNode"): # group-item
- if self.outdated(item):
- return self.OUTDATED_COLOR
+ elif role == QtCore.Qt.ForegroundRole:
+ role = self.foreground_role_by_column.get(col)
+ if role is None:
+ return None
- if self._hierarchy_view:
- # If current group is not outdated, check if any
- # outdated children.
- for _node in walk_hierarchy(item):
- if self.outdated(_node):
- return self.CHILD_OUTDATED_COLOR
- else:
+ if col != 0:
+ index = self.index(index.row(), 0, index.parent())
- if self._hierarchy_view:
- # Although this is not a group item, we still need
- # to distinguish which one contain outdated child.
- for _node in walk_hierarchy(item):
- if self.outdated(_node):
- return self.CHILD_OUTDATED_COLOR.darker(150)
-
- return self.GRAYOUT_COLOR
-
- if key == "Name" and not item.get("isGroupNode"):
- return self.GRAYOUT_COLOR
-
- # Add icons
- if role == QtCore.Qt.DecorationRole:
- if index.column() == 0:
- # Override color
- color = item.get("color", self._default_icon_color)
- if item.get("isGroupNode"): # group-item
- return qtawesome.icon("fa.folder", color=color)
- if item.get("isNotSet"):
- return qtawesome.icon("fa.exclamation-circle", color=color)
-
- return qtawesome.icon("fa.file-o", color=color)
-
- if index.column() == 3:
- # Product type icon
- return item.get("productTypeIcon", None)
-
- column_name = self.Columns[index.column()]
-
- if column_name == "group" and item.get("group"):
- return qtawesome.icon("fa.object-group",
- color=get_default_entity_icon_color())
-
- if item.get("isGroupNode"):
- if column_name == "active_site":
- provider = item.get("active_site_provider")
- return self._site_icons.get(provider)
-
- if column_name == "remote_site":
- provider = item.get("remote_site_provider")
- return self._site_icons.get(provider)
-
- if role == QtCore.Qt.DisplayRole and item.get("isGroupNode"):
- column_name = self.Columns[index.column()]
- progress = None
- if column_name == "active_site":
- progress = item.get("active_site_progress", 0)
- elif column_name == "remote_site":
- progress = item.get("remote_site_progress", 0)
- if progress is not None:
- return "{}%".format(max(progress, 0) * 100)
-
- if role == self.UniqueRole:
- return item["representation"] + item.get("objectName", "")
-
- return super(InventoryModel, self).data(index, role)
+ return super().data(index, role)
def set_hierarchy_view(self, state):
"""Set whether to display products in hierarchy view."""
@@ -165,299 +335,34 @@ class InventoryModel(TreeModel):
if state != self._hierarchy_view:
self._hierarchy_view = state
- def refresh(self, selected=None, containers=None):
- """Refresh the model"""
-
- # for debugging or testing, injecting items from outside
- if containers is None:
- containers = self._controller.get_containers()
-
- self.clear()
- if not selected or not self._hierarchy_view:
- self._add_containers(containers)
- return
-
- # Filter by cherry-picked items
- self._add_containers((
- container
- for container in containers
- if container["objectName"] in selected
- ))
-
- def _add_containers(self, containers, parent=None):
- """Add the items to the model.
-
- The items should be formatted similar to `api.ls()` returns, an item
- is then represented as:
- {"filename_v001.ma": [full/filename/of/loaded/filename_v001.ma,
- full/filename/of/loaded/filename_v001.ma],
- "nodetype" : "reference",
- "node": "referenceNode1"}
-
- Note: When performing an additional call to `add_items` it will *not*
- group the new items with previously existing item groups of the
- same type.
-
- Args:
- containers (generator): Container items.
- parent (Item, optional): Set this item as parent for the added
- items when provided. Defaults to the root of the model.
-
- Returns:
- node.Item: root node which has children added based on the data
- """
-
- project_name = get_current_project_name()
-
- self.beginResetModel()
-
- # Group by representation
- grouped = defaultdict(lambda: {"containers": list()})
- for container in containers:
- repre_id = container["representation"]
- grouped[repre_id]["containers"].append(container)
-
- (
- repres_by_id,
- versions_by_id,
- products_by_id,
- folders_by_id,
- ) = self._query_entities(project_name, set(grouped.keys()))
- # Add to model
- not_found = defaultdict(list)
- not_found_ids = []
- for repre_id, group_dict in sorted(grouped.items()):
- group_containers = group_dict["containers"]
- representation = repres_by_id.get(repre_id)
- if not representation:
- not_found["representation"].extend(group_containers)
- not_found_ids.append(repre_id)
+ def get_outdated_item_ids(self, ignore_hero=True):
+ outdated_item_ids = []
+ root_item = self.invisibleRootItem()
+ for row in range(root_item.rowCount()):
+ group_item = root_item.child(row)
+ if group_item.data(VERSION_IS_LATEST_ROLE):
continue
- version_entity = versions_by_id.get(representation["versionId"])
- if not version_entity:
- not_found["version"].extend(group_containers)
- not_found_ids.append(repre_id)
+ if ignore_hero and group_item.data(VERSION_IS_HERO_ROLE):
continue
- product_entity = products_by_id.get(version_entity["productId"])
- if not product_entity:
- not_found["product"].extend(group_containers)
- not_found_ids.append(repre_id)
- continue
+ for idx in range(group_item.rowCount()):
+ item = group_item.child(idx)
+ outdated_item_ids.append(item.data(ITEM_ID_ROLE))
+ return outdated_item_ids
- folder_entity = folders_by_id.get(product_entity["folderId"])
- if not folder_entity:
- not_found["folder"].extend(group_containers)
- not_found_ids.append(repre_id)
- continue
-
- group_dict.update({
- "representation": representation,
- "version": version_entity,
- "product": product_entity,
- "folder": folder_entity
- })
-
- for _repre_id in not_found_ids:
- grouped.pop(_repre_id)
-
- for where, group_containers in not_found.items():
- # create the group header
- group_node = Item()
- name = "< NOT FOUND - {} >".format(where)
- group_node["Name"] = name
- group_node["representation"] = name
- group_node["count"] = len(group_containers)
- group_node["isGroupNode"] = False
- group_node["isNotSet"] = True
-
- self.add_child(group_node, parent=parent)
-
- for container in group_containers:
- item_node = Item()
- item_node.update(container)
- item_node["Name"] = container.get("objectName", "NO NAME")
- item_node["isNotFound"] = True
- self.add_child(item_node, parent=group_node)
-
- # TODO Use product icons
- product_type_icon = qtawesome.icon(
- "fa.folder", color="#0091B2"
- )
- # Prepare site sync specific data
- progress_by_id = self._controller.get_representations_site_progress(
- set(grouped.keys())
- )
- sites_info = self._controller.get_sites_information()
-
- # Query the highest available version so the model can know
- # whether current version is currently up-to-date.
- highest_version_by_product_id = ayon_api.get_last_versions(
- project_name,
- product_ids={
- group["version"]["productId"] for group in grouped.values()
- },
- fields={"productId", "version"}
- )
- # Map value to `version` key
- highest_version_by_product_id = {
- product_id: version["version"]
- for product_id, version in highest_version_by_product_id.items()
- }
-
- for repre_id, group_dict in sorted(grouped.items()):
- group_containers = group_dict["containers"]
- repre_entity = group_dict["representation"]
- version_entity = group_dict["version"]
- folder_entity = group_dict["folder"]
- product_entity = group_dict["product"]
-
- product_type = product_entity["productType"]
-
- # create the group header
- group_node = Item()
- group_node["Name"] = "{}_{}: ({})".format(
- folder_entity["name"],
- product_entity["name"],
- repre_entity["name"]
- )
- group_node["representation"] = repre_id
-
- # Detect hero version type
- version = version_entity["version"]
- if version < 0:
- version = HeroVersionType(version)
- group_node["version"] = version
-
- # Check if the version is outdated.
- # Hero versions are never considered to be outdated.
- is_outdated = False
- if not isinstance(version, HeroVersionType):
- last_version = highest_version_by_product_id.get(
- version_entity["productId"])
- if last_version is not None:
- is_outdated = version_entity["version"] != last_version
- group_node["isOutdated"] = is_outdated
-
- group_node["productType"] = product_type or ""
- group_node["productTypeIcon"] = product_type_icon
- group_node["count"] = len(group_containers)
- group_node["isGroupNode"] = True
- group_node["group"] = product_entity["attrib"].get("productGroup")
-
- # Site sync specific data
- progress = progress_by_id[repre_id]
- group_node.update(sites_info)
- group_node["active_site_progress"] = progress["active_site"]
- group_node["remote_site_progress"] = progress["remote_site"]
-
- self.add_child(group_node, parent=parent)
-
- for container in group_containers:
- item_node = Item()
- item_node.update(container)
-
- # store the current version on the item
- item_node["version"] = version_entity["version"]
- item_node["version_entity"] = version_entity
-
- # Remapping namespace to item name.
- # Noted that the name key is capital "N", by doing this, we
- # can view namespace in GUI without changing container data.
- item_node["Name"] = container["namespace"]
-
- self.add_child(item_node, parent=group_node)
-
- self.endResetModel()
-
- return self._root_item
-
- def _query_entities(self, project_name, repre_ids):
- """Query entities for representations from containers.
-
- Returns:
- tuple[dict, dict, dict, dict]: Representation, version, product
- and folder documents by id.
- """
-
- repres_by_id = {}
- versions_by_id = {}
- products_by_id = {}
- folders_by_id = {}
- output = (
- repres_by_id,
- versions_by_id,
- products_by_id,
- folders_by_id,
- )
-
- filtered_repre_ids = set()
- for repre_id in repre_ids:
- # Filter out invalid representation ids
- # NOTE: This is added because scenes from OpenPype did contain
- # ObjectId from mongo.
- try:
- uuid.UUID(repre_id)
- filtered_repre_ids.add(repre_id)
- except ValueError:
- continue
- if not filtered_repre_ids:
- return output
-
- repre_entities = ayon_api.get_representations(project_name, repre_ids)
- repres_by_id.update({
- repre_entity["id"]: repre_entity
- for repre_entity in repre_entities
- })
- version_ids = {
- repre_entity["versionId"]
- for repre_entity in repres_by_id.values()
- }
- if not version_ids:
- return output
-
- versions_by_id.update({
- version_entity["id"]: version_entity
- for version_entity in ayon_api.get_versions(
- project_name, version_ids=version_ids
- )
- })
-
- product_ids = {
- version_entity["productId"]
- for version_entity in versions_by_id.values()
- }
- if not product_ids:
- return output
-
- products_by_id.update({
- product_entity["id"]: product_entity
- for product_entity in ayon_api.get_products(
- project_name, product_ids=product_ids
- )
- })
- folder_ids = {
- product_entity["folderId"]
- for product_entity in products_by_id.values()
- }
- if not folder_ids:
- return output
-
- folders_by_id.update({
- folder_entity["id"]: folder_entity
- for folder_entity in ayon_api.get_folders(
- project_name, folder_ids=folder_ids
- )
- })
- return output
+ def _clear_items(self):
+ root_item = self.invisibleRootItem()
+ root_item.removeRows(0, root_item.rowCount())
class FilterProxyModel(QtCore.QSortFilterProxyModel):
"""Filter model to where key column's value is in the filtered tags"""
def __init__(self, *args, **kwargs):
- super(FilterProxyModel, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
+ self.setDynamicSortFilter(True)
+ self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self._filter_outdated = False
self._hierarchy_view = False
@@ -467,28 +372,23 @@ class FilterProxyModel(QtCore.QSortFilterProxyModel):
# Always allow bottom entries (individual containers), since their
# parent group hidden if it wouldn't have been validated.
- rows = model.rowCount(source_index)
- if not rows:
+ if source_index.data(IS_CONTAINER_ITEM_ROLE):
return True
- # Filter by regex
- if hasattr(self, "filterRegExp"):
- regex = self.filterRegExp()
- else:
- regex = self.filterRegularExpression()
- pattern = regex.pattern()
- if pattern:
- pattern = re.escape(pattern)
-
- if not self._matches(row, parent, pattern):
- return False
-
if self._filter_outdated:
# When filtering to outdated we filter the up to date entries
# thus we "allow" them when they are outdated
- if not self._is_outdated(row, parent):
+ if source_index.data(VERSION_IS_LATEST_ROLE):
return False
+ # Filter by regex
+ if hasattr(self, "filterRegularExpression"):
+ regex = self.filterRegularExpression()
+ else:
+ regex = self.filterRegExp()
+
+ if not self._matches(row, parent, regex.pattern()):
+ return False
return True
def set_filter_outdated(self, state):
@@ -505,37 +405,6 @@ class FilterProxyModel(QtCore.QSortFilterProxyModel):
if state != self._hierarchy_view:
self._hierarchy_view = state
- def _is_outdated(self, row, parent):
- """Return whether row is outdated.
-
- A row is considered outdated if `isOutdated` data is true or not set.
-
- """
- def outdated(node):
- return node.get("isOutdated", True)
-
- index = self.sourceModel().index(row, self.filterKeyColumn(), parent)
-
- # The scene contents are grouped by "representation", e.g. the same
- # "representation" loaded twice is grouped under the same header.
- # Since the version check filters these parent groups we skip that
- # check for the individual children.
- has_parent = index.parent().isValid()
- if has_parent and not self._hierarchy_view:
- return True
-
- # Filter to those that have the different version numbers
- node = index.internalPointer()
- if outdated(node):
- return True
-
- if self._hierarchy_view:
- for _node in walk_hierarchy(node):
- if outdated(_node):
- return True
-
- return False
-
def _matches(self, row, parent, pattern):
"""Return whether row matches regex pattern.
@@ -548,38 +417,31 @@ class FilterProxyModel(QtCore.QSortFilterProxyModel):
bool
"""
+ if not pattern:
+ return True
+
+ flags = 0
+ if self.sortCaseSensitivity() == QtCore.Qt.CaseInsensitive:
+ flags = re.IGNORECASE
+
+ regex = re.compile(re.escape(pattern), flags=flags)
+
model = self.sourceModel()
column = self.filterKeyColumn()
role = self.filterRole()
- def matches(row, parent, pattern):
+ matches_queue = collections.deque()
+ matches_queue.append((row, parent))
+ while matches_queue:
+ queue_item = matches_queue.popleft()
+ row, parent = queue_item
+
index = model.index(row, column, parent)
- key = model.data(index, role)
- if re.search(pattern, key, re.IGNORECASE):
+ value = model.data(index, role)
+ if regex.search(value):
return True
- if matches(row, parent, pattern):
- return True
+ for idx in range(model.rowCount(index)):
+ matches_queue.append((idx, index))
- # Also allow if any of the children matches
- source_index = model.index(row, column, parent)
- rows = model.rowCount(source_index)
-
- if any(
- matches(idx, source_index, pattern)
- for idx in range(rows)
- ):
- return True
-
- if not self._hierarchy_view:
- return False
-
- for idx in range(rows):
- child_index = model.index(idx, column, source_index)
- child_rows = model.rowCount(child_index)
- return any(
- self._matches(child_idx, child_index, pattern)
- for child_idx in range(child_rows)
- )
-
- return True
+ return False
diff --git a/client/ayon_core/tools/sceneinventory/models/__init__.py b/client/ayon_core/tools/sceneinventory/models/__init__.py
index f840a45aa8..28bc7be4d4 100644
--- a/client/ayon_core/tools/sceneinventory/models/__init__.py
+++ b/client/ayon_core/tools/sceneinventory/models/__init__.py
@@ -1,6 +1,8 @@
+from .containers import ContainersModel
from .sitesync import SiteSyncModel
__all__ = (
+ "ContainersModel",
"SiteSyncModel",
)
diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py
new file mode 100644
index 0000000000..5230827ef6
--- /dev/null
+++ b/client/ayon_core/tools/sceneinventory/models/containers.py
@@ -0,0 +1,343 @@
+import uuid
+import collections
+
+import ayon_api
+from ayon_api.graphql import GraphQlQuery
+from ayon_core.host import ILoadHost
+
+
+# --- Implementation that should be in ayon-python-api ---
+# The implementation is not available in all versions of ayon-python-api.
+RepresentationHierarchy = collections.namedtuple(
+ "RepresentationHierarchy",
+ ("folder", "product", "version", "representation")
+)
+
+
+def representations_parent_ids_qraphql_query():
+ query = GraphQlQuery("RepresentationsHierarchyQuery")
+
+ project_name_var = query.add_variable("projectName", "String!")
+ repre_ids_var = query.add_variable("representationIds", "[String!]")
+
+ project_field = query.add_field("project")
+ project_field.set_filter("name", project_name_var)
+
+ repres_field = project_field.add_field_with_edges("representations")
+ repres_field.add_field("id")
+ repres_field.add_field("name")
+ repres_field.set_filter("ids", repre_ids_var)
+ version_field = repres_field.add_field("version")
+ version_field.add_field("id")
+ product_field = version_field.add_field("product")
+ product_field.add_field("id")
+ product_field.add_field("name")
+ product_field.add_field("productType")
+ product_attrib_field = product_field.add_field("attrib")
+ product_attrib_field.add_field("productGroup")
+ folder_field = product_field.add_field("folder")
+ folder_field.add_field("id")
+ folder_field.add_field("path")
+ return query
+
+
+def get_representations_hierarchy(project_name, representation_ids):
+ """Find representations parents by representation id.
+
+ Representation parent entities up to project.
+
+ Args:
+ project_name (str): Project where to look for entities.
+ representation_ids (Iterable[str]): Representation ids.
+
+ Returns:
+ dict[str, RepresentationParents]: Parent entities by
+ representation id.
+
+ """
+ if not representation_ids:
+ return {}
+
+ repre_ids = set(representation_ids)
+ output = {
+ repre_id: RepresentationHierarchy(None, None, None, None)
+ for repre_id in representation_ids
+ }
+
+ query = representations_parent_ids_qraphql_query()
+ query.set_variable_value("projectName", project_name)
+ query.set_variable_value("representationIds", list(repre_ids))
+
+ con = ayon_api.get_server_api_connection()
+ parsed_data = query.query(con)
+ for repre in parsed_data["project"]["representations"]:
+ repre_id = repre["id"]
+ version = repre.pop("version")
+ product = version.pop("product")
+ folder = product.pop("folder")
+
+ output[repre_id] = RepresentationHierarchy(
+ folder, product, version, repre
+ )
+
+ return output
+# --- END of ayon-python-api implementation ---
+
+
+class ContainerItem:
+ def __init__(
+ self,
+ representation_id,
+ loader_name,
+ namespace,
+ name,
+ object_name,
+ item_id
+ ):
+ self.representation_id = representation_id
+ self.loader_name = loader_name
+ self.object_name = object_name
+ self.namespace = namespace
+ self.name = name
+ self.item_id = item_id
+
+ @classmethod
+ def from_container_data(cls, container):
+ return cls(
+ representation_id=container["representation"],
+ loader_name=container["loader"],
+ namespace=container["namespace"],
+ name=container["name"],
+ object_name=container["objectName"],
+ item_id=uuid.uuid4().hex,
+ )
+
+
+class RepresentationInfo:
+ def __init__(
+ self,
+ folder_id,
+ folder_path,
+ product_id,
+ product_name,
+ product_type,
+ product_group,
+ version_id,
+ representation_name,
+ ):
+ self.folder_id = folder_id
+ self.folder_path = folder_path
+ self.product_id = product_id
+ self.product_name = product_name
+ self.product_type = product_type
+ self.product_group = product_group
+ self.version_id = version_id
+ self.representation_name = representation_name
+ self._is_valid = None
+
+ @property
+ def is_valid(self):
+ if self._is_valid is None:
+ self._is_valid = (
+ self.folder_id is not None
+ and self.product_id is not None
+ and self.version_id is not None
+ and self.representation_name is not None
+ )
+ return self._is_valid
+
+ @classmethod
+ def new_invalid(cls):
+ return cls(None, None, None, None, None, None, None, None)
+
+
+class VersionItem:
+ def __init__(self, version_id, product_id, version, status, is_latest):
+ self.version = version
+ self.version_id = version_id
+ self.product_id = product_id
+ self.version = version
+ self.status = status
+ self.is_latest = is_latest
+
+ @property
+ def is_hero(self):
+ return self.version < 0
+
+ @classmethod
+ def from_entity(cls, version_entity, is_latest):
+ return cls(
+ version_id=version_entity["id"],
+ product_id=version_entity["productId"],
+ version=version_entity["version"],
+ status=version_entity["status"],
+ is_latest=is_latest,
+ )
+
+
+class ContainersModel:
+ def __init__(self, controller):
+ self._controller = controller
+ self._items_cache = None
+ self._containers_by_id = {}
+ self._container_items_by_id = {}
+ self._version_items_by_product_id = {}
+ self._repre_info_by_id = {}
+
+ def reset(self):
+ self._items_cache = None
+ self._containers_by_id = {}
+ self._container_items_by_id = {}
+ self._version_items_by_product_id = {}
+ self._repre_info_by_id = {}
+
+ def get_containers(self):
+ self._update_cache()
+ return list(self._containers_by_id.values())
+
+ def get_containers_by_item_ids(self, item_ids):
+ return {
+ item_id: self._containers_by_id.get(item_id)
+ for item_id in item_ids
+ }
+
+ def get_container_items(self):
+ self._update_cache()
+ return list(self._items_cache)
+
+ def get_container_items_by_id(self, item_ids):
+ return {
+ item_id: self._container_items_by_id.get(item_id)
+ for item_id in item_ids
+ }
+
+ def get_representation_info_items(self, representation_ids):
+ output = {}
+ missing_repre_ids = set()
+ for repre_id in representation_ids:
+ try:
+ uuid.UUID(repre_id)
+ except ValueError:
+ output[repre_id] = RepresentationInfo.new_invalid()
+ continue
+
+ repre_info = self._repre_info_by_id.get(repre_id)
+ if repre_info is None:
+ missing_repre_ids.add(repre_id)
+ else:
+ output[repre_id] = repre_info
+
+ if not missing_repre_ids:
+ return output
+
+ project_name = self._controller.get_current_project_name()
+ repre_hierarchy_by_id = get_representations_hierarchy(
+ project_name, missing_repre_ids
+ )
+ for repre_id, repre_hierarchy in repre_hierarchy_by_id.items():
+ kwargs = {
+ "folder_id": None,
+ "folder_path": None,
+ "product_id": None,
+ "product_name": None,
+ "product_type": None,
+ "product_group": None,
+ "version_id": None,
+ "representation_name": None,
+ }
+ folder = repre_hierarchy.folder
+ product = repre_hierarchy.product
+ version = repre_hierarchy.version
+ repre = repre_hierarchy.representation
+ if folder:
+ kwargs["folder_id"] = folder["id"]
+ kwargs["folder_path"] = folder["path"]
+ if product:
+ group = product["attrib"]["productGroup"]
+ kwargs["product_id"] = product["id"]
+ kwargs["product_name"] = product["name"]
+ kwargs["product_type"] = product["productType"]
+ kwargs["product_group"] = group
+ if version:
+ kwargs["version_id"] = version["id"]
+ if repre:
+ kwargs["representation_name"] = repre["name"]
+
+ repre_info = RepresentationInfo(**kwargs)
+ self._repre_info_by_id[repre_id] = repre_info
+ output[repre_id] = repre_info
+ return output
+
+ def get_version_items(self, product_ids):
+ if not product_ids:
+ return {}
+
+ missing_ids = {
+ product_id
+ for product_id in product_ids
+ if product_id not in self._version_items_by_product_id
+ }
+ if missing_ids:
+ def version_sorted(entity):
+ return entity["version"]
+
+ project_name = self._controller.get_current_project_name()
+ version_entities_by_product_id = {
+ product_id: []
+ for product_id in missing_ids
+ }
+
+ version_entities = list(ayon_api.get_versions(
+ project_name,
+ product_ids=missing_ids,
+ fields={"id", "version", "productId", "status"}
+ ))
+ version_entities.sort(key=version_sorted)
+ for version_entity in version_entities:
+ product_id = version_entity["productId"]
+ version_entities_by_product_id[product_id].append(
+ version_entity
+ )
+
+ for product_id, version_entities in (
+ version_entities_by_product_id.items()
+ ):
+ last_version = abs(version_entities[-1]["version"])
+ version_items_by_id = {
+ entity["id"]: VersionItem.from_entity(
+ entity, abs(entity["version"]) == last_version
+ )
+ for entity in version_entities
+ }
+ self._version_items_by_product_id[product_id] = (
+ version_items_by_id
+ )
+
+ return {
+ product_id: dict(self._version_items_by_product_id[product_id])
+ for product_id in product_ids
+ }
+
+ def _update_cache(self):
+ if self._items_cache is not None:
+ return
+
+ host = self._controller.get_host()
+ if isinstance(host, ILoadHost):
+ containers = list(host.get_containers())
+ elif hasattr(host, "ls"):
+ containers = list(host.ls())
+ else:
+ containers = []
+ container_items = []
+ containers_by_id = {}
+ container_items_by_id = {}
+ for container in containers:
+ item = ContainerItem.from_container_data(container)
+ containers_by_id[item.item_id] = container
+ container_items_by_id[item.item_id] = item
+ container_items.append(item)
+
+ self._containers_by_id = containers_by_id
+ self._container_items_by_id = container_items_by_id
+ self._items_cache = container_items
diff --git a/client/ayon_core/tools/sceneinventory/select_version_dialog.py b/client/ayon_core/tools/sceneinventory/select_version_dialog.py
new file mode 100644
index 0000000000..1945d71a6d
--- /dev/null
+++ b/client/ayon_core/tools/sceneinventory/select_version_dialog.py
@@ -0,0 +1,216 @@
+import uuid
+
+from qtpy import QtWidgets, QtCore, QtGui
+
+from ayon_core.tools.utils.delegates import StatusDelegate
+
+from .model import (
+ ITEM_ID_ROLE,
+ STATUS_NAME_ROLE,
+ STATUS_SHORT_ROLE,
+ STATUS_COLOR_ROLE,
+ STATUS_ICON_ROLE,
+)
+
+
+class VersionOption:
+ def __init__(
+ self,
+ version,
+ label,
+ status_name,
+ status_short,
+ status_color
+ ):
+ self.version = version
+ self.label = label
+ self.status_name = status_name
+ self.status_short = status_short
+ self.status_color = status_color
+
+
+class SelectVersionModel(QtGui.QStandardItemModel):
+ def data(self, index, role=None):
+ if role is None:
+ role = QtCore.Qt.DisplayRole
+
+ index = self.index(index.row(), 0, index.parent())
+ return super().data(index, role)
+
+
+class SelectVersionComboBox(QtWidgets.QComboBox):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ combo_model = SelectVersionModel(0, 2)
+
+ self.setModel(combo_model)
+
+ combo_view = QtWidgets.QTreeView(self)
+ combo_view.setHeaderHidden(True)
+ combo_view.setIndentation(0)
+
+ self.setView(combo_view)
+
+ header = combo_view.header()
+ header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
+ header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
+
+ status_delegate = StatusDelegate(
+ STATUS_NAME_ROLE,
+ STATUS_SHORT_ROLE,
+ STATUS_COLOR_ROLE,
+ STATUS_ICON_ROLE,
+ )
+ combo_view.setItemDelegateForColumn(1, status_delegate)
+
+ self._combo_model = combo_model
+ self._combo_view = combo_view
+ self._status_delegate = status_delegate
+ self._items_by_id = {}
+
+ def paintEvent(self, event):
+ painter = QtWidgets.QStylePainter(self)
+ option = QtWidgets.QStyleOptionComboBox()
+ self.initStyleOption(option)
+ painter.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, option)
+ idx = self.currentIndex()
+ status_name = self.itemData(idx, STATUS_NAME_ROLE)
+ if status_name is None:
+ painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option)
+ return
+
+ painter.save()
+ text_field_rect = self.style().subControlRect(
+ QtWidgets.QStyle.CC_ComboBox,
+ option,
+ QtWidgets.QStyle.SC_ComboBoxEditField
+ )
+ adj_rect = text_field_rect.adjusted(1, 0, -1, 0)
+ painter.drawText(
+ adj_rect,
+ QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter,
+ option.currentText
+ )
+ metrics = QtGui.QFontMetrics(self.font())
+ text_width = metrics.width(option.currentText)
+ x_offset = text_width + 2
+ diff_width = adj_rect.width() - x_offset
+ if diff_width <= 0:
+ return
+
+ status_rect = adj_rect.adjusted(x_offset + 2, 0, 0, 0)
+ if diff_width < metrics.width(status_name):
+ status_name = self.itemData(idx, STATUS_SHORT_ROLE)
+
+ color = QtGui.QColor(self.itemData(idx, STATUS_COLOR_ROLE))
+
+ pen = painter.pen()
+ pen.setColor(color)
+ painter.setPen(pen)
+ painter.drawText(
+ status_rect,
+ QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter,
+ status_name
+ )
+
+ def set_current_index(self, index):
+ model = self._combo_view.model()
+ if index > model.rowCount():
+ return
+
+ self.setCurrentIndex(index)
+
+ def get_item_by_id(self, item_id):
+ return self._items_by_id[item_id]
+
+ def set_versions(self, version_options):
+ self._items_by_id = {}
+ model = self._combo_model
+ root_item = model.invisibleRootItem()
+ root_item.removeRows(0, root_item.rowCount())
+
+ new_items = []
+ for version_option in version_options:
+ item_id = uuid.uuid4().hex
+ item = QtGui.QStandardItem(version_option.label)
+ item.setColumnCount(root_item.columnCount())
+ item.setData(
+ version_option.status_name, STATUS_NAME_ROLE
+ )
+ item.setData(
+ version_option.status_short, STATUS_SHORT_ROLE
+ )
+ item.setData(
+ version_option.status_color, STATUS_COLOR_ROLE
+ )
+ item.setData(item_id, ITEM_ID_ROLE)
+
+ new_items.append(item)
+ self._items_by_id[item_id] = version_option
+
+ if new_items:
+ root_item.appendRows(new_items)
+
+
+class SelectVersionDialog(QtWidgets.QDialog):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setWindowTitle("Select version")
+
+ label_widget = QtWidgets.QLabel("Set version number to", self)
+ versions_combobox = SelectVersionComboBox(self)
+
+ btns_widget = QtWidgets.QWidget(self)
+
+ confirm_btn = QtWidgets.QPushButton("OK", btns_widget)
+ cancel_btn = QtWidgets.QPushButton("Cancel", btns_widget)
+
+ btns_layout = QtWidgets.QHBoxLayout(btns_widget)
+ btns_layout.setContentsMargins(0, 0, 0, 0)
+ btns_layout.addStretch(1)
+ btns_layout.addWidget(confirm_btn, 0)
+ btns_layout.addWidget(cancel_btn, 0)
+
+ main_layout = QtWidgets.QVBoxLayout(self)
+ main_layout.addWidget(label_widget, 0)
+ main_layout.addWidget(versions_combobox, 0)
+ main_layout.addWidget(btns_widget, 0)
+
+ confirm_btn.clicked.connect(self._on_confirm)
+ cancel_btn.clicked.connect(self._on_cancel)
+
+ self._selected_item = None
+ self._cancelled = False
+ self._versions_combobox = versions_combobox
+
+ def get_selected_item(self):
+ if self._cancelled:
+ return None
+ return self._selected_item
+
+ def set_versions(self, version_options):
+ self._versions_combobox.set_versions(version_options)
+
+ def select_index(self, index):
+ self._versions_combobox.set_current_index(index)
+
+ @classmethod
+ def ask_for_version(cls, version_options, index=None, parent=None):
+ dialog = cls(parent)
+ dialog.set_versions(version_options)
+ if index is not None:
+ dialog.select_index(index)
+ dialog.exec_()
+ return dialog.get_selected_item()
+
+ def _on_confirm(self):
+ self._cancelled = False
+ index = self._versions_combobox.currentIndex()
+ item_id = self._versions_combobox.itemData(index, ITEM_ID_ROLE)
+ self._selected_item = self._versions_combobox.get_item_by_id(item_id)
+ self.accept()
+
+ def _on_cancel(self):
+ self._cancelled = True
+ self.reject()
diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py
index 5cbd4daf70..770d0d903d 100644
--- a/client/ayon_core/tools/sceneinventory/view.py
+++ b/client/ayon_core/tools/sceneinventory/view.py
@@ -1,10 +1,7 @@
-import uuid
import collections
import logging
-import itertools
from functools import partial
-import ayon_api
from qtpy import QtWidgets, QtCore
import qtawesome
@@ -17,12 +14,27 @@ from ayon_core.pipeline import (
)
from ayon_core.tools.utils.lib import (
iter_model_rows,
- format_version
+ format_version,
+ preserve_expanded_rows,
+ preserve_selection,
)
+from ayon_core.tools.utils.delegates import StatusDelegate
from .switch_dialog import SwitchAssetDialog
-from .model import InventoryModel
-
+from .model import (
+ InventoryModel,
+ FilterProxyModel,
+ ITEM_UNIQUE_NAME_ROLE,
+ OBJECT_NAME_ROLE,
+ ITEM_ID_ROLE,
+ IS_CONTAINER_ITEM_ROLE,
+ STATUS_NAME_ROLE,
+ STATUS_SHORT_ROLE,
+ STATUS_COLOR_ROLE,
+ STATUS_ICON_ROLE,
+)
+from .delegates import VersionDelegate
+from .select_version_dialog import SelectVersionDialog, VersionOption
DEFAULT_COLOR = "#fb9c15"
@@ -43,185 +55,199 @@ class SceneInventoryView(QtWidgets.QTreeView):
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+ model = InventoryModel(controller)
+ proxy_model = FilterProxyModel()
+ proxy_model.setSourceModel(model)
+ proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
+
+ self.setModel(proxy_model)
+
+ version_delegate = VersionDelegate()
+ status_delegate = StatusDelegate(
+ STATUS_NAME_ROLE,
+ STATUS_SHORT_ROLE,
+ STATUS_COLOR_ROLE,
+ STATUS_ICON_ROLE,
+ )
+ for col, delegate in (
+ (model.version_col, version_delegate),
+ (model.status_col, status_delegate),
+ ):
+ self.setItemDelegateForColumn(col, delegate)
+
+ # set some nice default widths for the view
+ for col, width in model.width_by_column.items():
+ self.setColumnWidth(col, width)
+
+ sync_enabled = controller.is_sitesync_enabled()
+ self.setColumnHidden(model.active_site_col, not sync_enabled)
+ self.setColumnHidden(model.remote_site_col, not sync_enabled)
+
self.customContextMenuRequested.connect(self._show_right_mouse_menu)
+ self._model = model
+ self._proxy_model = proxy_model
+ self._version_delegate = version_delegate
+ self._status_delegate = status_delegate
+
self._hierarchy_view = False
self._selected = None
self._controller = controller
+ def refresh(self):
+ with preserve_expanded_rows(
+ tree_view=self,
+ role=ITEM_UNIQUE_NAME_ROLE
+ ):
+ with preserve_selection(
+ tree_view=self,
+ role=ITEM_UNIQUE_NAME_ROLE,
+ current_index=False
+ ):
+ kwargs = {}
+ # TODO do not touch view's inner attribute
+ if self._hierarchy_view:
+ kwargs["selected"] = self._selected
+ self._model.refresh(**kwargs)
+
+ def set_hierarchy_view(self, enabled):
+ self._proxy_model.set_hierarchy_view(enabled)
+ self._model.set_hierarchy_view(enabled)
+
+ def set_text_filter(self, text_filter):
+ if hasattr(self._proxy_model, "setFilterRegularExpression"):
+ self._proxy_model.setFilterRegularExpression(text_filter)
+ else:
+ self._proxy_model.setFilterRegExp(text_filter)
+
+ def set_filter_outdated(self, enabled):
+ self._proxy_model.set_filter_outdated(enabled)
+
+ def get_selected_indexes(self):
+ """Get the selected rows"""
+ indexes, _ = self._get_selected_indexes()
+ return indexes
+
+ def get_selected_item_ids(self):
+ return self._get_item_ids_from_indexes(
+ self.get_selected_indexes()
+ )
+
+ def get_selected_container_indexes(self):
+ return self._get_container_indexes(
+ self.get_selected_indexes()
+ )
+
+ def _get_selected_indexes(self):
+ selection_model = self.selectionModel()
+ indexes = selection_model.selectedRows()
+ active = self.currentIndex()
+ active = active.sibling(active.row(), 0)
+ if active not in indexes:
+ indexes.append(active)
+ return indexes, active
+
+ def _get_item_ids_from_indexes(self, indexes):
+ return {
+ index.data(ITEM_ID_ROLE)
+ for index in self._get_container_indexes(indexes)
+ }
+
def _set_hierarchy_view(self, enabled):
if enabled == self._hierarchy_view:
return
self._hierarchy_view = enabled
self.hierarchy_view_changed.emit(enabled)
- def _enter_hierarchy(self, items):
- self._selected = set(i["objectName"] for i in items)
+ def _enter_hierarchy(self, item_ids):
+ self._selected = set(item_ids)
self._set_hierarchy_view(True)
self.data_changed.emit()
self.expandToDepth(1)
- self.setStyleSheet("""
- QTreeView {
- border-color: #fb9c15;
- }
- """)
+ self.setStyleSheet("border-color: #fb9c15;")
def _leave_hierarchy(self):
self._set_hierarchy_view(False)
self.data_changed.emit()
- self.setStyleSheet("QTreeView {}")
+ self.setStyleSheet("")
- def _build_item_menu_for_selection(self, items, menu):
- # Exclude items that are "NOT FOUND" since setting versions, updating
- # and removal won't work for those items.
- items = [item for item in items if not item.get("isNotFound")]
- if not items:
+ def _build_item_menu_for_selection(self, menu, indexes, active_index):
+ item_ids = {
+ index.data(ITEM_ID_ROLE)
+ for index in indexes
+ }
+ item_ids.discard(None)
+ if not item_ids:
return
- # An item might not have a representation, for example when an item
- # is listed as "NOT FOUND"
- repre_ids = set()
- for item in items:
- repre_id = item["representation"]
- try:
- uuid.UUID(repre_id)
- repre_ids.add(repre_id)
- except ValueError:
- pass
-
- project_name = self._controller.get_current_project_name()
- repre_entities = ayon_api.get_representations(
- project_name,
- representation_ids=repre_ids,
- fields={"versionId"}
+ container_items_by_id = self._controller.get_container_items_by_id(
+ item_ids
)
- version_ids = {
- repre_entity["versionId"]
- for repre_entity in repre_entities
+ active_repre_id = None
+ if active_index is not None:
+ for index in self._get_container_indexes({active_index}):
+ item_id = index.data(ITEM_ID_ROLE)
+ container_item = container_items_by_id[item_id]
+ active_repre_id = container_item.representation_id
+ break
+
+ repre_info_by_id = self._controller.get_representation_info_items({
+ container_item.representation_id
+ for container_item in container_items_by_id.values()
+ })
+ valid_repre_ids = {
+ repre_id
+ for repre_id, repre_info in repre_info_by_id.items()
+ if repre_info.is_valid
}
- loaded_versions = ayon_api.get_versions(
- project_name, version_ids=version_ids
- )
-
- loaded_hero_versions = []
- versions_by_product_id = collections.defaultdict(list)
+ # Exclude items that are "NOT FOUND" since setting versions, updating
+ # and removal won't work for those items.
+ filtered_items = []
product_ids = set()
- for version_entity in loaded_versions:
- version = version_entity["version"]
- if version < 0:
- loaded_hero_versions.append(version_entity)
- else:
- product_id = version_entity["productId"]
- versions_by_product_id[product_id].append(version_entity)
- product_ids.add(product_id)
+ version_ids = set()
+ for container_item in container_items_by_id.values():
+ repre_id = container_item.representation_id
+ repre_info = repre_info_by_id.get(repre_id)
+ if repre_info and repre_info.is_valid:
+ filtered_items.append(container_item)
+ version_ids.add(repre_info.version_id)
+ product_ids.add(repre_info.product_id)
- all_versions = ayon_api.get_versions(
- project_name, product_ids=product_ids
+ # remove
+ remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR)
+ remove_action = QtWidgets.QAction(remove_icon, "Remove items", menu)
+ remove_action.triggered.connect(
+ lambda: self._show_remove_warning_dialog(item_ids))
+
+ if not filtered_items:
+ # Keep remove action for invalid items
+ menu.addAction(remove_action)
+ return
+
+ version_items_by_product_id = self._controller.get_version_items(
+ product_ids
)
- hero_versions = []
- version_entities = []
- for version_entity in all_versions:
- version = version_entity["version"]
- if version < 0:
- hero_versions.append(version_entity)
- else:
- version_entities.append(version_entity)
-
- has_loaded_hero_versions = len(loaded_hero_versions) > 0
- has_available_hero_version = len(hero_versions) > 0
has_outdated = False
+ has_loaded_hero_versions = False
+ has_available_hero_version = False
+ for version_items_by_id in version_items_by_product_id.values():
+ for version_item in version_items_by_id.values():
+ if version_item.is_hero:
+ has_available_hero_version = True
- for version_entity in version_entities:
- product_id = version_entity["productId"]
- current_versions = versions_by_product_id[product_id]
- for current_version in current_versions:
- if current_version["version"] < version_entity["version"]:
+ if version_item.version_id not in version_ids:
+ continue
+ if version_item.is_hero:
+ has_loaded_hero_versions = True
+
+ elif not version_item.is_latest:
has_outdated = True
- break
-
- if has_outdated:
- break
switch_to_versioned = None
if has_loaded_hero_versions:
- def _on_switch_to_versioned(items):
- repre_ids = {
- item["representation"]
- for item in items
- }
-
- repre_entities = ayon_api.get_representations(
- project_name,
- representation_ids=repre_ids,
- fields={"id", "versionId"}
- )
-
- version_id_by_repre_id = {}
- for repre_entity in repre_entities:
- repre_id = repre_entity["id"]
- version_id = repre_entity["versionId"]
- version_id_by_repre_id[repre_id] = version_id
- version_ids = set(version_id_by_repre_id.values())
-
- src_version_entity_by_id = {
- version_entity["id"]: version_entity
- for version_entity in ayon_api.get_versions(
- project_name,
- version_ids,
- fields={"productId", "version"}
- )
- }
- hero_versions_by_product_id = {}
- for version_entity in src_version_entity_by_id.values():
- version = version_entity["version"]
- if version < 0:
- product_id = version_entity["productId"]
- hero_versions_by_product_id[product_id] = abs(version)
-
- if not hero_versions_by_product_id:
- return
-
- standard_versions = ayon_api.get_versions(
- project_name,
- product_ids=hero_versions_by_product_id.keys(),
- versions=hero_versions_by_product_id.values()
- )
- standard_version_by_product_id = {
- product_id: {}
- for product_id in hero_versions_by_product_id.keys()
- }
- for version_entity in standard_versions:
- product_id = version_entity["productId"]
- version = version_entity["version"]
- standard_version_by_product_id[product_id][version] = (
- version_entity
- )
-
- # Specify version per item to update to
- update_items = []
- update_versions = []
- for item in items:
- repre_id = item["representation"]
- version_id = version_id_by_repre_id.get(repre_id)
- version_entity = src_version_entity_by_id.get(version_id)
- if not version_entity or version_entity["version"] >= 0:
- continue
- product_id = version_entity["productId"]
- version_entities_by_version = (
- standard_version_by_product_id[product_id]
- )
- new_version = hero_versions_by_product_id.get(product_id)
- new_version_entity = version_entities_by_version.get(
- new_version
- )
- if new_version_entity is not None:
- update_items.append(item)
- update_versions.append(new_version)
- self._update_containers(update_items, update_versions)
-
update_icon = qtawesome.icon(
"fa.asterisk",
color=DEFAULT_COLOR
@@ -232,7 +258,7 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu
)
switch_to_versioned.triggered.connect(
- lambda: _on_switch_to_versioned(items)
+ lambda: self._on_switch_to_versioned(item_ids)
)
update_to_latest_action = None
@@ -247,7 +273,9 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu
)
update_to_latest_action.triggered.connect(
- lambda: self._update_containers(items, version=-1)
+ lambda: self._update_containers_to_version(
+ item_ids, version=-1
+ )
)
change_to_hero = None
@@ -263,20 +291,23 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu
)
change_to_hero.triggered.connect(
- lambda: self._update_containers(
- items, version=HeroVersionType(-1)
+ lambda: self._update_containers_to_version(
+ item_ids, version=HeroVersionType(-1)
)
)
# set version
- set_version_icon = qtawesome.icon("fa.hashtag", color=DEFAULT_COLOR)
- set_version_action = QtWidgets.QAction(
- set_version_icon,
- "Set version",
- menu
- )
- set_version_action.triggered.connect(
- lambda: self._show_version_dialog(items))
+ set_version_action = None
+ if active_repre_id is not None:
+ set_version_icon = qtawesome.icon("fa.hashtag", color=DEFAULT_COLOR)
+ set_version_action = QtWidgets.QAction(
+ set_version_icon,
+ "Set version",
+ menu
+ )
+ set_version_action.triggered.connect(
+ lambda: self._show_version_dialog(item_ids, active_repre_id)
+ )
# switch folder
switch_folder_icon = qtawesome.icon("fa.sitemap", color=DEFAULT_COLOR)
@@ -286,13 +317,7 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu
)
switch_folder_action.triggered.connect(
- lambda: self._show_switch_dialog(items))
-
- # remove
- remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR)
- remove_action = QtWidgets.QAction(remove_icon, "Remove items", menu)
- remove_action.triggered.connect(
- lambda: self._show_remove_warning_dialog(items))
+ lambda: self._show_switch_dialog(item_ids))
# add the actions
if switch_to_versioned:
@@ -304,14 +329,15 @@ class SceneInventoryView(QtWidgets.QTreeView):
if change_to_hero:
menu.addAction(change_to_hero)
- menu.addAction(set_version_action)
+ if set_version_action is not None:
+ menu.addAction(set_version_action)
menu.addAction(switch_folder_action)
menu.addSeparator()
menu.addAction(remove_action)
- self._handle_sitesync(menu, repre_ids)
+ self._handle_sitesync(menu, valid_repre_ids)
def _handle_sitesync(self, menu, repre_ids):
"""Adds actions for download/upload when SyncServer is enabled
@@ -327,6 +353,9 @@ class SceneInventoryView(QtWidgets.QTreeView):
if not self._controller.is_sitesync_enabled():
return
+ if not repre_ids:
+ return
+
menu.addSeparator()
download_icon = qtawesome.icon("fa.download", color=DEFAULT_COLOR)
@@ -365,30 +394,35 @@ class SceneInventoryView(QtWidgets.QTreeView):
self.data_changed.emit()
- def _build_item_menu(self, items=None):
+ def _build_item_menu(self, indexes=None, active_index=None):
"""Create menu for the selected items"""
-
- if not items:
- items = []
-
menu = QtWidgets.QMenu(self)
- # add the actions
- self._build_item_menu_for_selection(items, menu)
-
# These two actions should be able to work without selection
# expand all items
- expandall_action = QtWidgets.QAction(menu, text="Expand all items")
- expandall_action.triggered.connect(self.expandAll)
+ expand_all_action = QtWidgets.QAction(menu, text="Expand all items")
+ expand_all_action.triggered.connect(self.expandAll)
# collapse all items
collapse_action = QtWidgets.QAction(menu, text="Collapse all items")
collapse_action.triggered.connect(self.collapseAll)
- menu.addAction(expandall_action)
+ if not indexes:
+ indexes = []
+
+ item_ids = {
+ index.data(ITEM_ID_ROLE)
+ for index in indexes
+ }
+ item_ids.discard(None)
+
+ # add the actions
+ self._build_item_menu_for_selection(menu, indexes, active_index)
+
+ menu.addAction(expand_all_action)
menu.addAction(collapse_action)
- custom_actions = self._get_custom_actions(containers=items)
+ custom_actions = self._get_custom_actions(item_ids)
if custom_actions:
submenu = QtWidgets.QMenu("Actions", self)
for action in custom_actions:
@@ -396,7 +430,10 @@ class SceneInventoryView(QtWidgets.QTreeView):
icon = qtawesome.icon("fa.%s" % action.icon, color=color)
action_item = QtWidgets.QAction(icon, action.label, submenu)
action_item.triggered.connect(
- partial(self._process_custom_action, action, items))
+ partial(
+ self._process_custom_action, action, item_ids
+ )
+ )
submenu.addAction(action_item)
@@ -421,9 +458,9 @@ class SceneInventoryView(QtWidgets.QTreeView):
menu
)
enter_hierarchy_action.triggered.connect(
- lambda: self._enter_hierarchy(items))
+ lambda: self._enter_hierarchy(item_ids))
- if items:
+ if indexes:
menu.addAction(enter_hierarchy_action)
if back_to_flat_action is not None:
@@ -431,11 +468,11 @@ class SceneInventoryView(QtWidgets.QTreeView):
return menu
- def _get_custom_actions(self, containers):
+ def _get_custom_actions(self, item_ids):
"""Get the registered Inventory Actions
Args:
- containers(list): collection of containers
+ item_ids (Iterable[str]): collection of containers
Returns:
list: collection of filter and initialized actions
@@ -448,29 +485,40 @@ class SceneInventoryView(QtWidgets.QTreeView):
# Fedd an empty dict if no selection, this will ensure the compat
# lookup always work, so plugin can interact with Scene Inventory
# reversely.
- containers = containers or [dict()]
+ if not item_ids:
+ containers = [dict()]
+ else:
+ containers_by_id = self._controller.get_containers_by_item_ids(
+ item_ids
+ )
+ containers = list(containers_by_id.values())
# Check which action will be available in the menu
Plugins = discover_inventory_actions()
- compatible = [p() for p in Plugins if
- any(p.is_compatible(c) for c in containers)]
+ compatible = [
+ p()
+ for p in Plugins
+ if any(p.is_compatible(c) for c in containers)
+ ]
return sorted(compatible, key=sorter)
- def _process_custom_action(self, action, containers):
+ def _process_custom_action(self, action, item_ids):
"""Run action and if results are returned positive update the view
If the result is list or dict, will select view items by the result.
Args:
action (InventoryAction): Inventory Action instance
- containers (list): Data of currently selected items
+ item_ids (Iterable[str]): Data of currently selected items
Returns:
None
"""
-
- result = action.process(containers)
+ containers_by_id = self._controller.get_containers_by_item_ids(
+ item_ids
+ )
+ result = action.process(list(containers_by_id.values()))
if result:
self.data_changed.emit()
@@ -498,17 +546,24 @@ class SceneInventoryView(QtWidgets.QTreeView):
if options.get("clear", True):
self.clearSelection()
- object_names = set(object_names)
- if (
- self._hierarchy_view
- and not self._selected.issuperset(object_names)
- ):
- # If any container not in current cherry-picked view, update
- # view before selecting them.
- self._selected.update(object_names)
- self.data_changed.emit()
-
model = self.model()
+ object_names = set(object_names)
+ if self._hierarchy_view:
+ item_ids = set()
+ for index in iter_model_rows(model):
+ if not index.data(IS_CONTAINER_ITEM_ROLE):
+ continue
+ if index.data(OBJECT_NAME_ROLE) in object_names:
+ item_id = index.data(ITEM_ID_ROLE)
+ if item_id:
+ item_ids.add(item_id)
+
+ if not self._selected.issuperset(item_ids):
+ # If any container not in current cherry-picked view, update
+ # view before selecting them.
+ self._selected.update(item_ids)
+ self.data_changed.emit()
+
selection_model = self.selectionModel()
select_mode = {
@@ -517,12 +572,10 @@ class SceneInventoryView(QtWidgets.QTreeView):
"toggle": QtCore.QItemSelectionModel.Toggle,
}[options.get("mode", "select")]
- for index in iter_model_rows(model, 0):
- item = index.data(InventoryModel.ItemRole)
- if item.get("isGroupNode"):
+ for index in iter_model_rows(model):
+ if not index.data(IS_CONTAINER_ITEM_ROLE):
continue
-
- name = item.get("objectName")
+ name = index.data(OBJECT_NAME_ROLE)
if name in object_names:
self.scrollTo(index) # Ensure item is visible
flags = select_mode | QtCore.QItemSelectionModel.Rows
@@ -539,177 +592,194 @@ class SceneInventoryView(QtWidgets.QTreeView):
globalpos = self.viewport().mapToGlobal(pos)
if not self.selectionModel().hasSelection():
- print("No selection")
# Build menu without selection, feed an empty list
menu = self._build_item_menu()
menu.exec_(globalpos)
return
- active = self.currentIndex() # index under mouse
- active = active.sibling(active.row(), 0) # get first column
-
- # move index under mouse
- indices = self.get_indices()
- if active in indices:
- indices.remove(active)
-
- indices.append(active)
+ indexes, active_index = self._get_selected_indexes()
# Extend to the sub-items
- all_indices = self._extend_to_children(indices)
- items = [dict(i.data(InventoryModel.ItemRole)) for i in all_indices
- if i.parent().isValid()]
+ all_indexes = self._extend_to_children(indexes)
- if self._hierarchy_view:
- # Ensure no group item
- items = [n for n in items if not n.get("isGroupNode")]
-
- menu = self._build_item_menu(items)
+ menu = self._build_item_menu(all_indexes, active_index)
menu.exec_(globalpos)
- def get_indices(self):
- """Get the selected rows"""
- selection_model = self.selectionModel()
- return selection_model.selectedRows()
+ def _get_container_indexes(self, indexes):
+ container_indexes = []
+ indexes_queue = collections.deque()
+ indexes_queue.extend(indexes)
+ # Ignore already added containers
+ items_ids = set()
+ while indexes_queue:
+ index = indexes_queue.popleft()
+ if index.data(IS_CONTAINER_ITEM_ROLE):
+ item_id = index.data(ITEM_ID_ROLE)
+ if item_id in items_ids:
+ continue
+ items_ids.add(item_id)
+ container_indexes.append(index)
+ continue
+ model = index.model()
+ for row in range(model.rowCount(index)):
+ child = model.index(row, 0, parent=index)
+ indexes_queue.append(child)
+ return container_indexes
- def _extend_to_children(self, indices):
+ def _extend_to_children(self, indexes):
"""Extend the indices to the children indices.
Top-level indices are extended to its children indices. Sub-items
are kept as is.
Args:
- indices (list): The indices to extend.
+ indexes (list): The indices to extend.
Returns:
list: The children indices
"""
- def get_children(i):
- model = i.model()
- rows = model.rowCount(parent=i)
- for row in range(rows):
- child = model.index(row, 0, parent=i)
- yield child
+ def get_children(index):
+ model = index.model()
+ for row in range(model.rowCount(index)):
+ yield model.index(row, 0, parent=index)
subitems = set()
- for i in indices:
- valid_parent = i.parent().isValid()
- if valid_parent and i not in subitems:
- subitems.add(i)
+ for index in indexes:
+ if index.parent().isValid() and index not in subitems:
+ subitems.add(index)
if self._hierarchy_view:
# Assume this is a group item
- for child in get_children(i):
+ for child in get_children(index):
subitems.add(child)
else:
# is top level item
- for child in get_children(i):
+ for child in get_children(index):
subitems.add(child)
return list(subitems)
- def _show_version_dialog(self, items):
+ def _show_version_dialog(self, item_ids, active_repre_id):
"""Create a dialog with the available versions for the selected file
Args:
- items (list): list of items to run the "set_version" for
+ item_ids (Iterable[str]): List of item ids to run the
+ "set_version" for.
+ active_repre_id (Union[str, None]): Active representation id.
Returns:
None
+
"""
-
- active = items[-1]
-
- project_name = self._controller.get_current_project_name()
- # Get available versions for active representation
- repre_entity = ayon_api.get_representation_by_id(
- project_name,
- active["representation"],
- fields={"versionId"}
+ container_items_by_id = self._controller.get_container_items_by_id(
+ item_ids
+ )
+ repre_ids = {
+ container_item.representation_id
+ for container_item in container_items_by_id.values()
+ }
+ repre_info_by_id = self._controller.get_representation_info_items(
+ repre_ids
)
- repre_version_entity = ayon_api.get_version_by_id(
- project_name,
- repre_entity["versionId"],
- fields={"productId"}
+ active_repre_info = repre_info_by_id[active_repre_id]
+ active_product_id = active_repre_info.product_id
+ active_version_id = active_repre_info.version_id
+ filtered_repre_info_by_id = {
+ repre_id: repre_info
+ for repre_id, repre_info in repre_info_by_id.items()
+ if repre_info.product_id == active_product_id
+ }
+ filtered_container_item_ids = {
+ item_id
+ for item_id, container_item in container_items_by_id.items()
+ if container_item.representation_id in filtered_repre_info_by_id
+ }
+ version_items_by_id = self._controller.get_version_items(
+ {active_product_id}
+ )[active_product_id]
+
+ def version_sorter(item):
+ hero_value = 0
+ version = item.version
+ if version < 0:
+ hero_value = 1
+ version = abs(version)
+ return version, hero_value
+
+ version_items = list(version_items_by_id.values())
+ version_items.sort(key=version_sorter, reverse=True)
+ status_items_by_name = {
+ status_item.name: status_item
+ for status_item in self._controller.get_project_status_items()
+ }
+
+ version_options = []
+ active_version_idx = 0
+ for idx, version_item in enumerate(version_items):
+ version = version_item.version
+ label = format_version(version)
+ if version_item.version_id == active_version_id:
+ active_version_idx = idx
+
+ status_name = version_item.status
+ status_short = None
+ status_color = None
+ status_item = status_items_by_name.get(status_name)
+ if status_item:
+ status_short = status_item.short
+ status_color = status_item.color
+ version_options.append(
+ VersionOption(
+ version,
+ label,
+ status_name,
+ status_short,
+ status_color,
+ )
+ )
+
+ version_option = SelectVersionDialog.ask_for_version(
+ version_options,
+ active_version_idx,
+ parent=self
)
-
- version_entities = list(ayon_api.get_versions(
- project_name,
- product_ids={repre_version_entity["productId"]},
- ))
- hero_version = None
- standard_versions = []
- for version_entity in version_entities:
- if version_entity["version"] < 0:
- hero_version = version_entity
- else:
- standard_versions.append(version_entity)
- standard_versions.sort(key=lambda item: item["version"])
- standard_versions.reverse()
-
- # Get index among the listed versions
- current_item = None
- current_version = active["version"]
- if isinstance(current_version, HeroVersionType):
- current_item = hero_version
- else:
- for version_entity in standard_versions:
- if version_entity["version"] == current_version:
- current_item = version_entity
- break
-
- all_versions = []
- if hero_version:
- all_versions.append(hero_version)
- all_versions.extend(standard_versions)
-
- if current_item:
- index = all_versions.index(current_item)
- else:
- index = 0
-
- versions_by_label = dict()
- labels = []
- for version_entity in all_versions:
- label = format_version(version_entity["version"])
- labels.append(label)
- versions_by_label[label] = version_entity["version"]
-
- label, state = QtWidgets.QInputDialog.getItem(
- self,
- "Set version..",
- "Set version number to",
- labels,
- current=index,
- editable=False
- )
- if not state:
+ if version_option is None:
return
- if label:
- version = versions_by_label[label]
- if version < 0:
- version = HeroVersionType(version)
- self._update_containers(items, version)
+ version = version_option.version
+ if version < 0:
+ version = HeroVersionType(version)
- def _show_switch_dialog(self, items):
+ self._update_containers_to_version(
+ filtered_container_item_ids, version
+ )
+
+ def _show_switch_dialog(self, item_ids):
"""Display Switch dialog"""
- dialog = SwitchAssetDialog(self._controller, self, items)
+ containers_by_id = self._controller.get_containers_by_item_ids(
+ item_ids
+ )
+ dialog = SwitchAssetDialog(
+ self._controller, self, list(containers_by_id.values())
+ )
dialog.switched.connect(self.data_changed.emit)
dialog.show()
- def _show_remove_warning_dialog(self, items):
+ def _show_remove_warning_dialog(self, item_ids):
"""Prompt a dialog to inform the user the action will remove items"""
-
+ containers_by_id = self._controller.get_containers_by_item_ids(
+ item_ids
+ )
+ containers = list(containers_by_id.values())
accept = QtWidgets.QMessageBox.Ok
buttons = accept | QtWidgets.QMessageBox.Cancel
state = QtWidgets.QMessageBox.question(
self,
"Are you sure?",
- "Are you sure you want to remove {} item(s)".format(len(items)),
+ f"Are you sure you want to remove {len(containers)} item(s)",
buttons=buttons,
defaultButton=accept
)
@@ -717,15 +787,15 @@ class SceneInventoryView(QtWidgets.QTreeView):
if state != accept:
return
- for item in items:
- remove_container(item)
+ for container in containers:
+ remove_container(container)
self.data_changed.emit()
- def _show_version_error_dialog(self, version, items):
+ def _show_version_error_dialog(self, version, item_ids):
"""Shows QMessageBox when version switch doesn't work
- Args:
- version: str or int or None
+ Args:
+ version: str or int or None
"""
if version == -1:
version_str = "latest"
@@ -745,7 +815,7 @@ class SceneInventoryView(QtWidgets.QTreeView):
"Switch Folder",
QtWidgets.QMessageBox.ActionRole
)
- switch_btn.clicked.connect(lambda: self._show_switch_dialog(items))
+ switch_btn.clicked.connect(lambda: self._show_switch_dialog(item_ids))
dialog.addButton(QtWidgets.QMessageBox.Cancel)
@@ -760,69 +830,115 @@ class SceneInventoryView(QtWidgets.QTreeView):
def update_all(self):
"""Update all items that are currently 'outdated' in the view"""
# Get the source model through the proxy model
- model = self.model().sourceModel()
-
- # Get all items from outdated groups
- outdated_items = []
- for index in iter_model_rows(model,
- column=0,
- include_root=False):
- item = index.data(model.ItemRole)
-
- if not item.get("isGroupNode"):
- continue
-
- # Only the group nodes contain the "highest_version" data and as
- # such we find only the groups and take its children.
- if not model.outdated(item):
- continue
-
- # Collect all children which we want to update
- children = item.children()
- outdated_items.extend(children)
-
- if not outdated_items:
+ item_ids = self._model.get_outdated_item_ids()
+ if not item_ids:
log.info("Nothing to update.")
return
# Trigger update to latest
- self._update_containers(outdated_items, version=-1)
+ self._update_containers_to_version(item_ids, version=-1)
- def _update_containers(self, items, version):
+ def _on_switch_to_versioned(self, item_ids):
+ containers_items_by_id = self._controller.get_container_items_by_id(
+ item_ids
+ )
+ repre_ids = {
+ container_item.representation_id
+ for container_item in containers_items_by_id.values()
+ }
+ repre_info_by_id = self._controller.get_representation_info_items(
+ repre_ids
+ )
+ product_ids = {
+ repre_info.product_id
+ for repre_info in repre_info_by_id.values()
+ if repre_info.is_valid
+ }
+ version_items_by_product_id = self._controller.get_version_items(
+ product_ids
+ )
+
+ update_containers = []
+ update_versions = []
+ for item_id, container_item in containers_items_by_id.items():
+ repre_id = container_item.representation_id
+ repre_info = repre_info_by_id[repre_id]
+ product_id = repre_info.product_id
+ version_items_id = version_items_by_product_id[product_id]
+ version_item = version_items_id.get(repre_info.version_id, {})
+ if not version_item or not version_item.is_hero:
+ continue
+ version = abs(version_item.version)
+ version_found = False
+ for version_item in version_items_id.values():
+ if version_item.is_hero:
+ continue
+ if version_item.version == version:
+ version_found = True
+ break
+
+ if not version_found:
+ continue
+
+ update_containers.append(container_item.item_id)
+ update_versions.append(version)
+
+ # Specify version per item to update to
+ self._update_containers(update_containers, update_versions)
+
+ def _update_containers(self, item_ids, versions):
"""Helper to update items to given version (or version per item)
If at least one item is specified this will always try to refresh
the inventory even if errors occurred on any of the items.
Arguments:
- items (list): Items to update
- version (int or list): Version to set to.
+ item_ids (Iterable[str]): Items to update
+ versions (Iterable[Union[int, HeroVersion]]): Version to set to.
This can be a list specifying a version for each item.
Like `update_container` version -1 sets the latest version
and HeroTypeVersion instances set the hero version.
"""
- if isinstance(version, (list, tuple)):
- # We allow a unique version to be specified per item. In that case
- # the length must match with the items
- assert len(items) == len(version), (
- "Number of items mismatches number of versions: "
- "{} items - {} versions".format(len(items), len(version))
- )
- versions = version
- else:
- # Repeat the same version infinitely
- versions = itertools.repeat(version)
+ # We allow a unique version to be specified per item. In that case
+ # the length must match with the items
+ assert len(item_ids) == len(versions), (
+ "Number of items mismatches number of versions: "
+ f"{len(item_ids)} items - {len(versions)} versions"
+ )
# Trigger update to latest
+ containers_by_id = self._controller.get_containers_by_item_ids(
+ item_ids
+ )
try:
- for item, item_version in zip(items, versions):
+ for item_id, item_version in zip(item_ids, versions):
+ container = containers_by_id[item_id]
try:
- update_container(item, item_version)
+ update_container(container, item_version)
except AssertionError:
- self._show_version_error_dialog(item_version, [item])
log.warning("Update failed", exc_info=True)
+ self._show_version_error_dialog(
+ item_version, [item_id]
+ )
finally:
# Always update the scene inventory view, even if errors occurred
self.data_changed.emit()
+
+ def _update_containers_to_version(self, item_ids, version):
+ """Helper to update items to given version (or version per item)
+
+ If at least one item is specified this will always try to refresh
+ the inventory even if errors occurred on any of the items.
+
+ Arguments:
+ item_ids (Iterable[str]): Items to update
+ version (Union[int, HeroVersion]): Version to set to.
+ This can be a list specifying a version for each item.
+ Like `update_container` version -1 sets the latest version
+ and HeroTypeVersion instances set the hero version.
+
+ """
+ versions = [version for _ in range(len(item_ids))]
+ self._update_containers(item_ids, versions)
diff --git a/client/ayon_core/tools/sceneinventory/window.py b/client/ayon_core/tools/sceneinventory/window.py
index 555db3a17c..58ff0c3b6d 100644
--- a/client/ayon_core/tools/sceneinventory/window.py
+++ b/client/ayon_core/tools/sceneinventory/window.py
@@ -2,17 +2,10 @@ from qtpy import QtWidgets, QtCore, QtGui
import qtawesome
from ayon_core import style, resources
-from ayon_core.tools.utils.lib import (
- preserve_expanded_rows,
- preserve_selection,
-)
+from ayon_core.tools.utils import PlaceholderLineEdit
+
from ayon_core.tools.sceneinventory import SceneInventoryController
-from .delegates import VersionDelegate
-from .model import (
- InventoryModel,
- FilterProxyModel
-)
from .view import SceneInventoryView
@@ -20,7 +13,7 @@ class SceneInventoryWindow(QtWidgets.QDialog):
"""Scene Inventory window"""
def __init__(self, controller=None, parent=None):
- super(SceneInventoryWindow, self).__init__(parent)
+ super().__init__(parent)
if controller is None:
controller = SceneInventoryController()
@@ -33,10 +26,9 @@ class SceneInventoryWindow(QtWidgets.QDialog):
self.resize(1100, 480)
- # region control
-
filter_label = QtWidgets.QLabel("Search", self)
- text_filter = QtWidgets.QLineEdit(self)
+ text_filter = PlaceholderLineEdit(self)
+ text_filter.setPlaceholderText("Filter by name...")
outdated_only_checkbox = QtWidgets.QCheckBox(
"Filter to outdated", self
@@ -44,52 +36,30 @@ class SceneInventoryWindow(QtWidgets.QDialog):
outdated_only_checkbox.setToolTip("Show outdated files only")
outdated_only_checkbox.setChecked(False)
- icon = qtawesome.icon("fa.arrow-up", color="white")
+ update_all_icon = qtawesome.icon("fa.arrow-up", color="white")
update_all_button = QtWidgets.QPushButton(self)
update_all_button.setToolTip("Update all outdated to latest version")
- update_all_button.setIcon(icon)
+ update_all_button.setIcon(update_all_icon)
- icon = qtawesome.icon("fa.refresh", color="white")
+ refresh_icon = qtawesome.icon("fa.refresh", color="white")
refresh_button = QtWidgets.QPushButton(self)
refresh_button.setToolTip("Refresh")
- refresh_button.setIcon(icon)
+ refresh_button.setIcon(refresh_icon)
- control_layout = QtWidgets.QHBoxLayout()
- control_layout.addWidget(filter_label)
- control_layout.addWidget(text_filter)
- control_layout.addWidget(outdated_only_checkbox)
- control_layout.addWidget(update_all_button)
- control_layout.addWidget(refresh_button)
-
- model = InventoryModel(controller)
- proxy = FilterProxyModel()
- proxy.setSourceModel(model)
- proxy.setDynamicSortFilter(True)
- proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
+ headers_widget = QtWidgets.QWidget(self)
+ headers_layout = QtWidgets.QHBoxLayout(headers_widget)
+ headers_layout.setContentsMargins(0, 0, 0, 0)
+ headers_layout.addWidget(filter_label, 0)
+ headers_layout.addWidget(text_filter, 1)
+ headers_layout.addWidget(outdated_only_checkbox, 0)
+ headers_layout.addWidget(update_all_button, 0)
+ headers_layout.addWidget(refresh_button, 0)
view = SceneInventoryView(controller, self)
- view.setModel(proxy)
- sync_enabled = controller.is_sitesync_enabled()
- view.setColumnHidden(model.active_site_col, not sync_enabled)
- view.setColumnHidden(model.remote_site_col, not sync_enabled)
-
- # set some nice default widths for the view
- view.setColumnWidth(0, 250) # name
- view.setColumnWidth(1, 55) # version
- view.setColumnWidth(2, 55) # count
- view.setColumnWidth(3, 150) # product type
- view.setColumnWidth(4, 120) # group
- view.setColumnWidth(5, 150) # loader
-
- # apply delegates
- version_delegate = VersionDelegate(controller, self)
- column = model.Columns.index("version")
- view.setItemDelegateForColumn(column, version_delegate)
-
- layout = QtWidgets.QVBoxLayout(self)
- layout.addLayout(control_layout)
- layout.addWidget(view)
+ main_layout = QtWidgets.QVBoxLayout(self)
+ main_layout.addWidget(headers_widget, 0)
+ main_layout.addWidget(view, 1)
show_timer = QtCore.QTimer()
show_timer.setInterval(0)
@@ -114,12 +84,8 @@ class SceneInventoryWindow(QtWidgets.QDialog):
self._update_all_button = update_all_button
self._outdated_only_checkbox = outdated_only_checkbox
self._view = view
- self._model = model
- self._proxy = proxy
- self._version_delegate = version_delegate
self._first_show = True
- self._first_refresh = True
def showEvent(self, event):
super(SceneInventoryWindow, self).showEvent(event)
@@ -139,29 +105,16 @@ class SceneInventoryWindow(QtWidgets.QDialog):
whilst trying to name an instance.
"""
+ pass
def _on_refresh_request(self):
"""Signal callback to trigger 'refresh' without any arguments."""
self.refresh()
- def refresh(self, containers=None):
- self._first_refresh = False
+ def refresh(self):
self._controller.reset()
- with preserve_expanded_rows(
- tree_view=self._view,
- role=self._model.UniqueRole
- ):
- with preserve_selection(
- tree_view=self._view,
- role=self._model.UniqueRole,
- current_index=False
- ):
- kwargs = {"containers": containers}
- # TODO do not touch view's inner attribute
- if self._view._hierarchy_view:
- kwargs["selected"] = self._view._selected
- self._model.refresh(**kwargs)
+ self._view.refresh()
def _on_show_timer(self):
if self._show_counter < 3:
@@ -171,17 +124,13 @@ class SceneInventoryWindow(QtWidgets.QDialog):
self.refresh()
def _on_hierarchy_view_change(self, enabled):
- self._proxy.set_hierarchy_view(enabled)
- self._model.set_hierarchy_view(enabled)
+ self._view.set_hierarchy_view(enabled)
def _on_text_filter_change(self, text_filter):
- if hasattr(self._proxy, "setFilterRegExp"):
- self._proxy.setFilterRegExp(text_filter)
- else:
- self._proxy.setFilterRegularExpression(text_filter)
+ self._view.set_text_filter(text_filter)
def _on_outdated_state_change(self):
- self._proxy.set_filter_outdated(
+ self._view.set_filter_outdated(
self._outdated_only_checkbox.isChecked()
)
diff --git a/client/ayon_core/tools/tray/tray.py b/client/ayon_core/tools/tray/tray.py
index 957518afe4..eca87eb11d 100644
--- a/client/ayon_core/tools/tray/tray.py
+++ b/client/ayon_core/tools/tray/tray.py
@@ -447,8 +447,10 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def initialize_addons(self):
self._initializing_addons = True
- self.tray_man.initialize_addons()
- self._initializing_addons = False
+ try:
+ self.tray_man.initialize_addons()
+ finally:
+ self._initializing_addons = False
def _click_timer_timeout(self):
self._click_timer.stop()
diff --git a/client/ayon_core/tools/utils/delegates.py b/client/ayon_core/tools/utils/delegates.py
index 1147074b77..b296f952e0 100644
--- a/client/ayon_core/tools/utils/delegates.py
+++ b/client/ayon_core/tools/utils/delegates.py
@@ -2,7 +2,7 @@ import time
from datetime import datetime
import logging
-from qtpy import QtWidgets
+from qtpy import QtWidgets, QtGui
log = logging.getLogger(__name__)
@@ -106,3 +106,80 @@ class PrettyTimeDelegate(QtWidgets.QStyledItemDelegate):
def displayText(self, value, locale):
if value is not None:
return pretty_timestamp(value)
+
+
+class StatusDelegate(QtWidgets.QStyledItemDelegate):
+ """Delegate showing status name and short name."""
+ def __init__(
+ self,
+ status_name_role,
+ status_short_name_role,
+ status_color_role,
+ status_icon_role,
+ *args, **kwargs
+ ):
+ super().__init__(*args, **kwargs)
+ self.status_name_role = status_name_role
+ self.status_short_name_role = status_short_name_role
+ self.status_color_role = status_color_role
+ self.status_icon_role = status_icon_role
+
+ 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 = self._get_status_name(index)
+ if padded_text_rect.width() < fm.width(text):
+ text = self._get_status_short_name(index)
+
+ fg_color = self._get_status_color(index)
+ pen = painter.pen()
+ pen.setColor(fg_color)
+ painter.setPen(pen)
+
+ painter.drawText(
+ padded_text_rect,
+ option.displayAlignment,
+ text
+ )
+
+ painter.restore()
+
+ def _get_status_name(self, index):
+ return index.data(self.status_name_role)
+
+ def _get_status_short_name(self, index):
+ return index.data(self.status_short_name_role)
+
+ def _get_status_color(self, index):
+ return QtGui.QColor(index.data(self.status_color_role))
+
+ def _get_status_icon(self, index):
+ if self.status_icon_role is not None:
+ return index.data(self.status_icon_role)
+ return None
diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py
index d56b370d75..323b5c07e1 100644
--- a/client/ayon_core/tools/utils/lib.py
+++ b/client/ayon_core/tools/utils/lib.py
@@ -1,6 +1,7 @@
import os
import sys
import contextlib
+import collections
from functools import partial
from qtpy import QtWidgets, QtCore, QtGui
@@ -196,16 +197,16 @@ def get_openpype_qt_app():
return get_ayon_qt_app()
-def iter_model_rows(model, column, include_root=False):
+def iter_model_rows(model, column=0, include_root=False):
"""Iterate over all row indices in a model"""
- indices = [QtCore.QModelIndex()] # start iteration at root
-
- for index in indices:
+ indexes_queue = collections.deque()
+ # start iteration at root
+ indexes_queue.append(QtCore.QModelIndex())
+ while indexes_queue:
+ index = indexes_queue.popleft()
# Add children to the iterations
- child_rows = model.rowCount(index)
- for child_row in range(child_rows):
- child_index = model.index(child_row, column, index)
- indices.append(child_index)
+ for child_row in range(model.rowCount(index)):
+ indexes_queue.append(model.index(child_row, column, index))
if not include_root and not index.isValid():
continue
diff --git a/client/ayon_core/tools/workfiles/abstract.py b/client/ayon_core/tools/workfiles/abstract.py
index c9eb9004e3..f345e20dca 100644
--- a/client/ayon_core/tools/workfiles/abstract.py
+++ b/client/ayon_core/tools/workfiles/abstract.py
@@ -13,8 +13,10 @@ class WorkfileInfo:
task_id (str): Task id.
filepath (str): Filepath.
filesize (int): File size.
- creation_time (int): Creation time (timestamp).
- modification_time (int): Modification time (timestamp).
+ creation_time (float): Creation time (timestamp).
+ modification_time (float): Modification time (timestamp).
+ created_by (Union[str, none]): User who created the file.
+ updated_by (Union[str, none]): User who last updated the file.
note (str): Note.
"""
@@ -26,6 +28,8 @@ class WorkfileInfo:
filesize,
creation_time,
modification_time,
+ created_by,
+ updated_by,
note,
):
self.folder_id = folder_id
@@ -34,6 +38,8 @@ class WorkfileInfo:
self.filesize = filesize
self.creation_time = creation_time
self.modification_time = modification_time
+ self.created_by = created_by
+ self.updated_by = updated_by
self.note = note
def to_data(self):
@@ -50,6 +56,8 @@ class WorkfileInfo:
"filesize": self.filesize,
"creation_time": self.creation_time,
"modification_time": self.modification_time,
+ "created_by": self.created_by,
+ "updated_by": self.updated_by,
"note": self.note,
}
@@ -212,6 +220,7 @@ class FileItem:
dirpath (str): Directory path of file.
filename (str): Filename.
modified (float): Modified timestamp.
+ created_by (Optional[str]): Username.
representation_id (Optional[str]): Representation id of published
workfile.
filepath (Optional[str]): Prepared filepath.
@@ -223,6 +232,8 @@ class FileItem:
dirpath,
filename,
modified,
+ created_by=None,
+ updated_by=None,
representation_id=None,
filepath=None,
exists=None
@@ -230,6 +241,8 @@ class FileItem:
self.filename = filename
self.dirpath = dirpath
self.modified = modified
+ self.created_by = created_by
+ self.updated_by = updated_by
self.representation_id = representation_id
self._filepath = filepath
self._exists = exists
@@ -269,6 +282,7 @@ class FileItem:
"filename": self.filename,
"dirpath": self.dirpath,
"modified": self.modified,
+ "created_by": self.created_by,
"representation_id": self.representation_id,
"filepath": self.filepath,
"exists": self.exists,
@@ -522,6 +536,16 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon):
pass
+ @abstractmethod
+ def get_user_items_by_name(self):
+ """Get user items available on AYON server.
+
+ Returns:
+ Dict[str, UserItem]: User items by username.
+
+ """
+ pass
+
# Host information
@abstractmethod
def get_workfile_extensions(self):
diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py
index 3048e6be94..8fa9135bc0 100644
--- a/client/ayon_core/tools/workfiles/control.py
+++ b/client/ayon_core/tools/workfiles/control.py
@@ -19,6 +19,7 @@ from ayon_core.tools.common_models import (
HierarchyModel,
HierarchyExpectedSelection,
ProjectsModel,
+ UsersModel,
)
from .abstract import (
@@ -161,6 +162,7 @@ class BaseWorkfileController(
self._save_is_enabled = True
# Expected selected folder and task
+ self._users_model = self._create_users_model()
self._expected_selection = self._create_expected_selection_obj()
self._selection_model = self._create_selection_model()
self._projects_model = self._create_projects_model()
@@ -176,6 +178,12 @@ class BaseWorkfileController(
def is_host_valid(self):
return self._host_is_valid
+ def _create_users_model(self):
+ return UsersModel(self)
+
+ def _create_workfiles_model(self):
+ return WorkfilesModel(self)
+
def _create_expected_selection_obj(self):
return WorkfilesToolExpectedSelection(self)
@@ -188,9 +196,6 @@ class BaseWorkfileController(
def _create_hierarchy_model(self):
return HierarchyModel(self)
- def _create_workfiles_model(self):
- return WorkfilesModel(self)
-
@property
def event_system(self):
"""Inner event system for workfiles tool controller.
@@ -272,6 +277,9 @@ class BaseWorkfileController(
{"enabled": enabled}
)
+ def get_user_items_by_name(self):
+ return self._users_model.get_user_items_by_name()
+
# Host information
def get_workfile_extensions(self):
host = self._host
diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py
index 5f59b99b22..c93bbb6637 100644
--- a/client/ayon_core/tools/workfiles/models/workfiles.py
+++ b/client/ayon_core/tools/workfiles/models/workfiles.py
@@ -6,6 +6,7 @@ import arrow
import ayon_api
from ayon_api.operations import OperationsSession
+from ayon_core.lib import get_ayon_username
from ayon_core.pipeline.template_data import (
get_template_data,
get_task_template_data,
@@ -23,6 +24,8 @@ from ayon_core.tools.workfiles.abstract import (
WorkfileInfo,
)
+_NOT_SET = object()
+
class CommentMatcher(object):
"""Use anatomy and work file data to parse comments from filenames.
@@ -188,10 +191,17 @@ class WorkareaModel:
if ext not in self._extensions:
continue
- modified = os.path.getmtime(filepath)
- items.append(
- FileItem(workdir, filename, modified)
+ workfile_info = self._controller.get_workfile_info(
+ folder_id, task_id, filepath
)
+ modified = os.path.getmtime(filepath)
+ items.append(FileItem(
+ workdir,
+ filename,
+ modified,
+ workfile_info.created_by,
+ workfile_info.updated_by,
+ ))
return items
def _get_template_key(self, fill_data):
@@ -439,6 +449,7 @@ class WorkfileEntitiesModel:
self._controller = controller
self._cache = {}
self._items = {}
+ self._current_username = _NOT_SET
def _get_workfile_info_identifier(
self, folder_id, task_id, rootless_path
@@ -459,8 +470,12 @@ class WorkfileEntitiesModel:
self, folder_id, task_id, workfile_info, filepath
):
note = ""
+ created_by = None
+ updated_by = None
if workfile_info:
note = workfile_info["attrib"].get("description") or ""
+ created_by = workfile_info.get("createdBy")
+ updated_by = workfile_info.get("updatedBy")
filestat = os.stat(filepath)
return WorkfileInfo(
@@ -470,6 +485,8 @@ class WorkfileEntitiesModel:
filesize=filestat.st_size,
creation_time=filestat.st_ctime,
modification_time=filestat.st_mtime,
+ created_by=created_by,
+ updated_by=updated_by,
note=note
)
@@ -481,7 +498,7 @@ class WorkfileEntitiesModel:
for workfile_info in ayon_api.get_workfiles_info(
self._controller.get_current_project_name(),
task_ids=[task_id],
- fields=["id", "path", "attrib"],
+ fields=["id", "path", "attrib", "createdBy", "updatedBy"],
):
workfile_identifier = self._get_workfile_info_identifier(
folder_id, task_id, workfile_info["path"]
@@ -525,18 +542,32 @@ class WorkfileEntitiesModel:
self._items.pop(identifier, None)
return
- if note is None:
- return
-
old_note = workfile_info.get("attrib", {}).get("note")
new_workfile_info = copy.deepcopy(workfile_info)
- attrib = new_workfile_info.setdefault("attrib", {})
- attrib["description"] = note
+ update_data = {}
+ if note is not None and old_note != note:
+ update_data["attrib"] = {"description": note}
+ attrib = new_workfile_info.setdefault("attrib", {})
+ attrib["description"] = note
+
+ username = self._get_current_username()
+ # Automatically fix 'createdBy' and 'updatedBy' fields
+ # NOTE both fields were not automatically filled by server
+ # until 1.1.3 release.
+ if workfile_info.get("createdBy") is None:
+ update_data["createdBy"] = username
+ new_workfile_info["createdBy"] = username
+
+ if workfile_info.get("updatedBy") != username:
+ update_data["updatedBy"] = username
+ new_workfile_info["updatedBy"] = username
+
+ if not update_data:
+ return
+
self._cache[identifier] = new_workfile_info
self._items.pop(identifier, None)
- if old_note == note:
- return
project_name = self._controller.get_current_project_name()
@@ -545,7 +576,7 @@ class WorkfileEntitiesModel:
project_name,
"workfile",
workfile_info["id"],
- {"attrib": {"description": note}},
+ update_data,
)
session.commit()
@@ -554,13 +585,18 @@ class WorkfileEntitiesModel:
project_name = self._controller.get_current_project_name()
+ username = self._get_current_username()
workfile_info = {
"path": rootless_path,
"taskId": task_id,
"attrib": {
"extension": extension,
"description": note
- }
+ },
+ # TODO remove 'createdBy' and 'updatedBy' fields when server is
+ # or above 1.1.3 .
+ "createdBy": username,
+ "updatedBy": username,
}
session = OperationsSession()
@@ -568,6 +604,11 @@ class WorkfileEntitiesModel:
session.commit()
return workfile_info
+ def _get_current_username(self):
+ if self._current_username is _NOT_SET:
+ self._current_username = get_ayon_username()
+ return self._current_username
+
class PublishWorkfilesModel:
"""Model for handling of published workfiles.
@@ -599,7 +640,7 @@ class PublishWorkfilesModel:
return self._cached_repre_extensions
def _file_item_from_representation(
- self, repre_entity, project_anatomy, task_name=None
+ self, repre_entity, project_anatomy, author, task_name=None
):
if task_name is not None:
task_info = repre_entity["context"].get("task")
@@ -634,6 +675,8 @@ class PublishWorkfilesModel:
dirpath,
filename,
created_at.float_timestamp,
+ author,
+ None,
repre_entity["id"]
)
@@ -643,9 +686,9 @@ class PublishWorkfilesModel:
# Get subset docs of folder
product_entities = ayon_api.get_products(
project_name,
- folder_ids=[folder_id],
- product_types=["workfile"],
- fields=["id", "name"]
+ folder_ids={folder_id},
+ product_types={"workfile"},
+ fields={"id", "name"}
)
output = []
@@ -657,25 +700,33 @@ class PublishWorkfilesModel:
version_entities = ayon_api.get_versions(
project_name,
product_ids=product_ids,
- fields=["id", "productId"]
+ fields={"id", "author"}
)
- version_ids = {version["id"] for version in version_entities}
- if not version_ids:
+ versions_by_id = {
+ version["id"]: version
+ for version in version_entities
+ }
+ if not versions_by_id:
return output
# Query representations of filtered versions and add filter for
# extension
repre_entities = ayon_api.get_representations(
project_name,
- version_ids=version_ids
+ version_ids=set(versions_by_id)
)
project_anatomy = self._controller.project_anatomy
# Filter queried representations by task name if task is set
file_items = []
for repre_entity in repre_entities:
+ version_id = repre_entity["versionId"]
+ version_entity = versions_by_id[version_id]
file_item = self._file_item_from_representation(
- repre_entity, project_anatomy, task_name
+ repre_entity,
+ project_anatomy,
+ version_entity["author"],
+ task_name,
)
if file_item is not None:
file_items.append(file_item)
diff --git a/client/ayon_core/tools/workfiles/widgets/files_widget_published.py b/client/ayon_core/tools/workfiles/widgets/files_widget_published.py
index bf36d790e9..2ce8569a9b 100644
--- a/client/ayon_core/tools/workfiles/widgets/files_widget_published.py
+++ b/client/ayon_core/tools/workfiles/widgets/files_widget_published.py
@@ -13,7 +13,8 @@ from .utils import BaseOverlayFrame
REPRE_ID_ROLE = QtCore.Qt.UserRole + 1
FILEPATH_ROLE = QtCore.Qt.UserRole + 2
-DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 3
+AUTHOR_ROLE = QtCore.Qt.UserRole + 3
+DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 4
class PublishedFilesModel(QtGui.QStandardItemModel):
@@ -23,13 +24,19 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
controller (AbstractWorkfilesFrontend): The control object.
"""
+ columns = [
+ "Name",
+ "Author",
+ "Date Modified",
+ ]
+ date_modified_col = columns.index("Date Modified")
+
def __init__(self, controller):
super(PublishedFilesModel, self).__init__()
- self.setColumnCount(2)
-
- self.setHeaderData(0, QtCore.Qt.Horizontal, "Name")
- self.setHeaderData(1, QtCore.Qt.Horizontal, "Date Modified")
+ self.setColumnCount(len(self.columns))
+ for idx, label in enumerate(self.columns):
+ self.setHeaderData(idx, QtCore.Qt.Horizontal, label)
controller.register_event_callback(
"selection.task.changed",
@@ -185,6 +192,8 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
self._remove_empty_item()
self._remove_missing_context_item()
+ user_items_by_name = self._controller.get_user_items_by_name()
+
items_to_remove = set(self._items_by_id.keys())
new_items = []
for file_item in file_items:
@@ -205,8 +214,15 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
else:
flags = QtCore.Qt.NoItemFlags
+ author = file_item.created_by
+ user_item = user_items_by_name.get(author)
+ if user_item is not None and user_item.full_name:
+ author = user_item.full_name
+
item.setFlags(flags)
+
item.setData(file_item.filepath, FILEPATH_ROLE)
+ item.setData(author, AUTHOR_ROLE)
item.setData(file_item.modified, DATE_MODIFIED_ROLE)
self._items_by_id[repre_id] = item
@@ -225,22 +241,30 @@ class PublishedFilesModel(QtGui.QStandardItemModel):
# Use flags of first column for all columns
if index.column() != 0:
index = self.index(index.row(), 0, index.parent())
- return super(PublishedFilesModel, self).flags(index)
+ return super().flags(index)
def data(self, index, role=None):
if role is None:
role = QtCore.Qt.DisplayRole
# Handle roles for first column
- if index.column() == 1:
- if role == QtCore.Qt.DecorationRole:
- return None
+ col = index.column()
+ if col != 1:
+ return super().data(index, role)
- if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
+ if role == QtCore.Qt.DecorationRole:
+ return None
+
+ if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
+ if col == 1:
+ role = AUTHOR_ROLE
+ elif col == 2:
role = DATE_MODIFIED_ROLE
- index = self.index(index.row(), 0, index.parent())
+ else:
+ return None
+ index = self.index(index.row(), 0, index.parent())
- return super(PublishedFilesModel, self).data(index, role)
+ return super().data(index, role)
class SelectContextOverlay(BaseOverlayFrame):
@@ -295,7 +319,7 @@ class PublishedFilesWidget(QtWidgets.QWidget):
view.setModel(proxy_model)
time_delegate = PrettyTimeDelegate()
- view.setItemDelegateForColumn(1, time_delegate)
+ view.setItemDelegateForColumn(model.date_modified_col, time_delegate)
# Default to a wider first filename column it is what we mostly care
# about and the date modified is relatively small anyway.
diff --git a/client/ayon_core/tools/workfiles/widgets/files_widget_workarea.py b/client/ayon_core/tools/workfiles/widgets/files_widget_workarea.py
index fe6abee951..5c102dcdd4 100644
--- a/client/ayon_core/tools/workfiles/widgets/files_widget_workarea.py
+++ b/client/ayon_core/tools/workfiles/widgets/files_widget_workarea.py
@@ -10,7 +10,8 @@ from ayon_core.tools.utils.delegates import PrettyTimeDelegate
FILENAME_ROLE = QtCore.Qt.UserRole + 1
FILEPATH_ROLE = QtCore.Qt.UserRole + 2
-DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 3
+AUTHOR_ROLE = QtCore.Qt.UserRole + 3
+DATE_MODIFIED_ROLE = QtCore.Qt.UserRole + 4
class WorkAreaFilesModel(QtGui.QStandardItemModel):
@@ -21,14 +22,20 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
"""
refreshed = QtCore.Signal()
+ columns = [
+ "Name",
+ "Author",
+ "Date Modified",
+ ]
+ date_modified_col = columns.index("Date Modified")
def __init__(self, controller):
super(WorkAreaFilesModel, self).__init__()
- self.setColumnCount(2)
+ self.setColumnCount(len(self.columns))
- self.setHeaderData(0, QtCore.Qt.Horizontal, "Name")
- self.setHeaderData(1, QtCore.Qt.Horizontal, "Date Modified")
+ for idx, label in enumerate(self.columns):
+ self.setHeaderData(idx, QtCore.Qt.Horizontal, label)
controller.register_event_callback(
"selection.folder.changed",
@@ -186,6 +193,7 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
return
self._remove_empty_item()
self._remove_missing_context_item()
+ user_items_by_name = self._controller.get_user_items_by_name()
items_to_remove = set(self._items_by_filename.keys())
new_items = []
@@ -205,7 +213,13 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
item.setData(file_item.filename, QtCore.Qt.DisplayRole)
item.setData(file_item.filename, FILENAME_ROLE)
+ updated_by = file_item.updated_by
+ user_item = user_items_by_name.get(updated_by)
+ if user_item is not None and user_item.full_name:
+ updated_by = user_item.full_name
+
item.setData(file_item.filepath, FILEPATH_ROLE)
+ item.setData(updated_by, AUTHOR_ROLE)
item.setData(file_item.modified, DATE_MODIFIED_ROLE)
self._items_by_filename[file_item.filename] = item
@@ -224,22 +238,30 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
# Use flags of first column for all columns
if index.column() != 0:
index = self.index(index.row(), 0, index.parent())
- return super(WorkAreaFilesModel, self).flags(index)
+ return super().flags(index)
def data(self, index, role=None):
if role is None:
role = QtCore.Qt.DisplayRole
# Handle roles for first column
- if index.column() == 1:
- if role == QtCore.Qt.DecorationRole:
- return None
+ col = index.column()
+ if col == 0:
+ return super().data(index, role)
- if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
+ if role == QtCore.Qt.DecorationRole:
+ return None
+
+ if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
+ if col == 1:
+ role = AUTHOR_ROLE
+ elif col == 2:
role = DATE_MODIFIED_ROLE
- index = self.index(index.row(), 0, index.parent())
+ else:
+ return None
+ index = self.index(index.row(), 0, index.parent())
- return super(WorkAreaFilesModel, self).data(index, role)
+ return super().data(index, role)
def set_published_mode(self, published_mode):
if self._published_mode == published_mode:
@@ -279,7 +301,7 @@ class WorkAreaFilesWidget(QtWidgets.QWidget):
view.setModel(proxy_model)
time_delegate = PrettyTimeDelegate()
- view.setItemDelegateForColumn(1, time_delegate)
+ view.setItemDelegateForColumn(model.date_modified_col, time_delegate)
# Default to a wider first filename column it is what we mostly care
# about and the date modified is relatively small anyway.
diff --git a/client/ayon_core/tools/workfiles/widgets/side_panel.py b/client/ayon_core/tools/workfiles/widgets/side_panel.py
index 5085f4701e..53fdf0e0ac 100644
--- a/client/ayon_core/tools/workfiles/widgets/side_panel.py
+++ b/client/ayon_core/tools/workfiles/widgets/side_panel.py
@@ -147,13 +147,38 @@ class SidePanelWidget(QtWidgets.QWidget):
workfile_info.creation_time)
modification_time = datetime.datetime.fromtimestamp(
workfile_info.modification_time)
+
+ user_items_by_name = self._controller.get_user_items_by_name()
+
+ def convert_username(username):
+ user_item = user_items_by_name.get(username)
+ if user_item is not None and user_item.full_name:
+ return user_item.full_name
+ return username
+
+ created_lines = [
+ creation_time.strftime(datetime_format)
+ ]
+ if workfile_info.created_by:
+ created_lines.insert(
+ 0, convert_username(workfile_info.created_by)
+ )
+
+ modified_lines = [
+ modification_time.strftime(datetime_format)
+ ]
+ if workfile_info.updated_by:
+ modified_lines.insert(
+ 0, convert_username(workfile_info.updated_by)
+ )
+
lines = (
"Size:",
size_value,
"Created:",
- creation_time.strftime(datetime_format),
+ "
".join(created_lines),
"Modified:",
- modification_time.strftime(datetime_format)
+ "
".join(modified_lines),
)
self._orig_note = note
self._note_input.setPlainText(note)
diff --git a/client/ayon_core/tools/workfiles/widgets/window.py b/client/ayon_core/tools/workfiles/widgets/window.py
index 1cfae7ec90..8bcff66f50 100644
--- a/client/ayon_core/tools/workfiles/widgets/window.py
+++ b/client/ayon_core/tools/workfiles/widgets/window.py
@@ -107,7 +107,7 @@ class WorkfilesToolWindow(QtWidgets.QWidget):
split_widget.addWidget(tasks_widget)
split_widget.addWidget(col_3_widget)
split_widget.addWidget(side_panel)
- split_widget.setSizes([255, 160, 455, 175])
+ split_widget.setSizes([255, 175, 550, 190])
body_layout.addWidget(split_widget)
@@ -169,7 +169,7 @@ class WorkfilesToolWindow(QtWidgets.QWidget):
# Force focus on the open button by default, required for Houdini.
self._files_widget.setFocus()
- self.resize(1200, 600)
+ self.resize(1260, 600)
def _create_col_1_widget(self, controller, parent):
col_widget = QtWidgets.QWidget(parent)
diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py
index a60de0493a..e4297e2000 100644
--- a/client/ayon_core/version.py
+++ b/client/ayon_core/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring AYON core addon version."""
-__version__ = "0.3.1-dev.1"
+__version__ = "0.3.3-dev.1"
diff --git a/client/pyproject.toml b/client/pyproject.toml
index 1a0ad7e5f2..5e811321f8 100644
--- a/client/pyproject.toml
+++ b/client/pyproject.toml
@@ -16,7 +16,7 @@ aiohttp_json_rpc = "*" # TVPaint server
aiohttp-middlewares = "^2.0.0"
wsrpc_aiohttp = "^3.1.1" # websocket server
Click = "^8"
-OpenTimelineIO = "0.14.1"
+OpenTimelineIO = "0.16.0"
opencolorio = "2.2.1"
Pillow = "9.5.0"
pynput = "^1.7.2" # Timers manager - TODO remove
diff --git a/package.py b/package.py
index 79450d029f..73f7174b6f 100644
--- a/package.py
+++ b/package.py
@@ -1,11 +1,12 @@
name = "core"
title = "Core"
-version = "0.3.1-dev.1"
+version = "0.3.3-dev.1"
client_dir = "ayon_core"
plugin_for = ["ayon_server"]
-requires = [
- "~ayon_server-1.0.3+<2.0.0",
-]
+ayon_server_version = ">=1.0.3,<2.0.0"
+ayon_launcher_version = ">=1.0.2"
+ayon_required_addons = {}
+ayon_compatible_addons = {}
diff --git a/pyproject.toml b/pyproject.toml
index 4726bef41a..63d7434c06 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -108,6 +108,10 @@ line-ending = "auto"
# Ignore words that are not in the dictionary.
ignore-words-list = "ayon,ynput,parms,parm,hda,developpement,ue"
+# Ignore lines that contain this regex. This is hack for missing inline ignore.
+# Remove with next codespell release (>2.2.6)
+ignore-regex = ".*codespell:ignore.*"
+
skip = "./.*,./package/*,*/vendor/*,*/unreal/integration/*,*/aftereffects/api/extension/js/libs/*"
count = true
quiet-level = 3
diff --git a/server/__init__.py b/server/__init__.py
index 152cc77218..d60f50f471 100644
--- a/server/__init__.py
+++ b/server/__init__.py
@@ -1,6 +1,12 @@
+from typing import Any
+
from ayon_server.addons import BaseServerAddon
-from .settings import CoreSettings, DEFAULT_VALUES
+from .settings import (
+ CoreSettings,
+ DEFAULT_VALUES,
+ convert_settings_overrides,
+)
class CoreAddon(BaseServerAddon):
@@ -9,3 +15,14 @@ class CoreAddon(BaseServerAddon):
async def get_default_settings(self):
settings_model_cls = self.get_settings_model()
return settings_model_cls(**DEFAULT_VALUES)
+
+ async def convert_settings_overrides(
+ self,
+ source_version: str,
+ overrides: dict[str, Any],
+ ) -> dict[str, Any]:
+ convert_settings_overrides(source_version, overrides)
+ # Use super conversion
+ return await super().convert_settings_overrides(
+ source_version, overrides
+ )
diff --git a/server/settings/__init__.py b/server/settings/__init__.py
index 527a2bdc0c..4bb21a9644 100644
--- a/server/settings/__init__.py
+++ b/server/settings/__init__.py
@@ -1,7 +1,10 @@
from .main import CoreSettings, DEFAULT_VALUES
+from .conversion import convert_settings_overrides
__all__ = (
"CoreSettings",
"DEFAULT_VALUES",
+
+ "convert_settings_overrides",
)
diff --git a/server/settings/conversion.py b/server/settings/conversion.py
new file mode 100644
index 0000000000..f513738603
--- /dev/null
+++ b/server/settings/conversion.py
@@ -0,0 +1,86 @@
+import copy
+from typing import Any
+
+from .publish_plugins import DEFAULT_PUBLISH_VALUES
+
+
+def _convert_imageio_configs_0_3_1(overrides):
+ """Imageio config settings did change to profiles since 0.3.1. ."""
+ imageio_overrides = overrides.get("imageio") or {}
+ if (
+ "ocio_config" not in imageio_overrides
+ or "filepath" not in imageio_overrides["ocio_config"]
+ ):
+ return
+
+ ocio_config = imageio_overrides.pop("ocio_config")
+
+ filepath = ocio_config["filepath"]
+ if not filepath:
+ return
+ first_filepath = filepath[0]
+ ocio_config_profiles = imageio_overrides.setdefault(
+ "ocio_config_profiles", []
+ )
+ base_value = {
+ "type": "builtin_path",
+ "product_name": "",
+ "host_names": [],
+ "task_names": [],
+ "task_types": [],
+ "custom_path": "",
+ "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio"
+ }
+ if first_filepath in (
+ "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio",
+ "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio",
+ ):
+ base_value["type"] = "builtin_path"
+ base_value["builtin_path"] = first_filepath
+ else:
+ base_value["type"] = "custom_path"
+ base_value["custom_path"] = first_filepath
+
+ ocio_config_profiles.append(base_value)
+
+
+def _convert_validate_version_0_3_3(publish_overrides):
+ """ValidateVersion plugin changed in 0.3.3."""
+ if "ValidateVersion" not in publish_overrides:
+ return
+
+ validate_version = publish_overrides["ValidateVersion"]
+ # Already new settings
+ if "plugin_state_profiles" in validate_version:
+ return
+
+ # Use new default profile as base
+ profile = copy.deepcopy(
+ DEFAULT_PUBLISH_VALUES["ValidateVersion"]["plugin_state_profiles"][0]
+ )
+ # Copy values from old overrides to new overrides
+ for key in {
+ "enabled",
+ "optional",
+ "active",
+ }:
+ if key not in validate_version:
+ continue
+ profile[key] = validate_version.pop(key)
+
+ validate_version["plugin_state_profiles"] = [profile]
+
+
+def _conver_publish_plugins(overrides):
+ if "publish" not in overrides:
+ return
+ _convert_validate_version_0_3_3(overrides["publish"])
+
+
+def convert_settings_overrides(
+ source_version: str,
+ overrides: dict[str, Any],
+) -> dict[str, Any]:
+ _convert_imageio_configs_0_3_1(overrides)
+ _conver_publish_plugins(overrides)
+ return overrides
diff --git a/server/settings/main.py b/server/settings/main.py
index 28a69e182d..40e16e7e91 100644
--- a/server/settings/main.py
+++ b/server/settings/main.py
@@ -54,9 +54,67 @@ class CoreImageIOFileRulesModel(BaseSettingsModel):
return value
-class CoreImageIOConfigModel(BaseSettingsModel):
- filepath: list[str] = SettingsField(
- default_factory=list, title="Config path"
+def _ocio_config_profile_types():
+ return [
+ {"value": "builtin_path", "label": "AYON built-in OCIO config"},
+ {"value": "custom_path", "label": "Path to OCIO config"},
+ {"value": "product_name", "label": "Published product"},
+ ]
+
+
+def _ocio_built_in_paths():
+ return [
+ {
+ "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio",
+ "label": "ACES 1.2",
+ "description": "Aces 1.2 OCIO config file."
+ },
+ {
+ "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio",
+ "label": "Nuke default",
+ },
+ ]
+
+
+class CoreImageIOConfigProfilesModel(BaseSettingsModel):
+ _layout = "expanded"
+ host_names: list[str] = SettingsField(
+ default_factory=list,
+ title="Host names"
+ )
+ task_types: list[str] = SettingsField(
+ default_factory=list,
+ title="Task types",
+ enum_resolver=task_types_enum
+ )
+ task_names: list[str] = SettingsField(
+ default_factory=list,
+ title="Task names"
+ )
+ type: str = SettingsField(
+ title="Profile type",
+ enum_resolver=_ocio_config_profile_types,
+ conditionalEnum=True,
+ default="builtin_path",
+ section="---",
+ )
+ builtin_path: str = SettingsField(
+ "ACES 1.2",
+ title="Built-in OCIO config",
+ enum_resolver=_ocio_built_in_paths,
+ )
+ custom_path: str = SettingsField(
+ "",
+ title="OCIO config path",
+ description="Path to OCIO config. Anatomy formatting is supported.",
+ )
+ product_name: str = SettingsField(
+ "",
+ title="Product name",
+ description=(
+ "Published product name to get OCIO config from. "
+ "Partial match is supported."
+ ),
)
@@ -65,9 +123,8 @@ class CoreImageIOBaseModel(BaseSettingsModel):
False,
title="Enable Color Management"
)
- ocio_config: CoreImageIOConfigModel = SettingsField(
- default_factory=CoreImageIOConfigModel,
- title="OCIO config"
+ ocio_config_profiles: list[CoreImageIOConfigProfilesModel] = SettingsField(
+ default_factory=list, title="OCIO config profiles"
)
file_rules: CoreImageIOFileRulesModel = SettingsField(
default_factory=CoreImageIOFileRulesModel,
@@ -186,12 +243,17 @@ class CoreSettings(BaseSettingsModel):
DEFAULT_VALUES = {
"imageio": {
"activate_global_color_management": False,
- "ocio_config": {
- "filepath": [
- "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio",
- "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio"
- ]
- },
+ "ocio_config_profiles": [
+ {
+ "host_names": [],
+ "task_types": [],
+ "task_names": [],
+ "type": "builtin_path",
+ "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio",
+ "custom_path": "",
+ "product_name": "",
+ }
+ ],
"file_rules": {
"activate_global_file_rules": False,
"rules": [
@@ -199,42 +261,57 @@ DEFAULT_VALUES = {
"name": "example",
"pattern": ".*(beauty).*",
"colorspace": "ACES - ACEScg",
- "ext": "exr"
+ "ext": "exr",
}
- ]
- }
+ ],
+ },
},
"studio_name": "",
"studio_code": "",
- "environments": "{\n\"STUDIO_SW\": {\n \"darwin\": \"/mnt/REPO_SW\",\n \"linux\": \"/mnt/REPO_SW\",\n \"windows\": \"P:/REPO_SW\"\n }\n}",
+ "environments": json.dumps(
+ {
+ "STUDIO_SW": {
+ "darwin": "/mnt/REPO_SW",
+ "linux": "/mnt/REPO_SW",
+ "windows": "P:/REPO_SW"
+ }
+ },
+ indent=4
+ ),
"tools": DEFAULT_TOOLS_VALUES,
"version_start_category": {
"profiles": []
},
"publish": DEFAULT_PUBLISH_VALUES,
- "project_folder_structure": json.dumps({
- "__project_root__": {
- "prod": {},
- "resources": {
- "footage": {
- "plates": {},
- "offline": {}
+ "project_folder_structure": json.dumps(
+ {
+ "__project_root__": {
+ "prod": {},
+ "resources": {
+ "footage": {
+ "plates": {},
+ "offline": {}
+ },
+ "audio": {},
+ "art_dept": {}
},
- "audio": {},
- "art_dept": {}
- },
- "editorial": {},
- "assets": {
- "characters": {},
- "locations": {}
- },
- "shots": {}
- }
- }, indent=4),
+ "editorial": {},
+ "assets": {
+ "characters": {},
+ "locations": {}
+ },
+ "shots": {}
+ }
+ },
+ indent=4
+ ),
"project_plugins": {
"windows": [],
"darwin": [],
"linux": []
},
- "project_environments": "{}"
+ "project_environments": json.dumps(
+ {},
+ indent=4
+ )
}
diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py
index e61bf6986b..2640a3db37 100644
--- a/server/settings/publish_plugins.py
+++ b/server/settings/publish_plugins.py
@@ -59,6 +59,28 @@ class CollectFramesFixDefModel(BaseSettingsModel):
)
+class PluginStateByHostModelProfile(BaseSettingsModel):
+ _layout = "expanded"
+ # Filtering
+ host_names: list[str] = SettingsField(
+ default_factory=list,
+ title="Host names"
+ )
+ # Profile values
+ enabled: bool = SettingsField(True, title="Enabled")
+ optional: bool = SettingsField(True, title="Optional")
+ active: bool = SettingsField(True, title="Active")
+
+
+class PluginStateByHostModel(BaseSettingsModel):
+ _isGroup = True
+ plugin_state_profiles: list[PluginStateByHostModelProfile] = SettingsField(
+ default_factory=list,
+ title="Plugin enable state profiles",
+ description="Change plugin state based on host name."
+ )
+
+
class ValidateIntentProfile(BaseSettingsModel):
_layout = "expanded"
hosts: list[str] = SettingsField(default_factory=list, title="Host names")
@@ -536,7 +558,7 @@ class ExtractBurninProfile(BaseSettingsModel):
_layout = "expanded"
product_types: list[str] = SettingsField(
default_factory=list,
- title="Produt types"
+ title="Product types"
)
hosts: list[str] = SettingsField(
default_factory=list,
@@ -766,9 +788,17 @@ class PublishPuginsModel(BaseSettingsModel):
default_factory=ValidateBaseModel,
title="Validate Editorial Asset Name"
)
- ValidateVersion: ValidateBaseModel = SettingsField(
- default_factory=ValidateBaseModel,
- title="Validate Version"
+ ValidateVersion: PluginStateByHostModel = SettingsField(
+ default_factory=PluginStateByHostModel,
+ title="Validate Version",
+ description=(
+ "Validate that product version to integrate"
+ " is newer than latest version in AYON."
+ )
+ )
+ ValidateOutdatedContainers: PluginStateByHostModel = SettingsField(
+ default_factory=PluginStateByHostModel,
+ title="Validate Containers"
)
ValidateIntent: ValidateIntentModel = SettingsField(
default_factory=ValidateIntentModel,
@@ -851,9 +881,40 @@ DEFAULT_PUBLISH_VALUES = {
"active": True
},
"ValidateVersion": {
- "enabled": True,
- "optional": False,
- "active": True
+ "plugin_state_profiles": [
+ {
+ "host_names": [
+ "aftereffects",
+ "blender",
+ "houdini",
+ "maya",
+ "nuke",
+ "photoshop",
+ ],
+ "enabled": True,
+ "optional": False,
+ "active": True
+ }
+ ]
+ },
+ "ValidateOutdatedContainers": {
+ "plugin_state_profiles": [
+ {
+ # Default host names are based on original
+ # filter of ValidateContainer pyblish plugin
+ "host_names": [
+ "maya",
+ "houdini",
+ "nuke",
+ "harmony",
+ "photoshop",
+ "aftereffects"
+ ],
+ "enabled": True,
+ "optional": True,
+ "active": True
+ }
+ ]
},
"ValidateIntent": {
"enabled": False,
diff --git a/server_addon/aftereffects/package.py b/server_addon/aftereffects/package.py
index a680b37602..7a2f9bc7af 100644
--- a/server_addon/aftereffects/package.py
+++ b/server_addon/aftereffects/package.py
@@ -1,3 +1,3 @@
name = "aftereffects"
title = "AfterEffects"
-version = "0.1.3"
+version = "0.1.4"
diff --git a/server_addon/aftereffects/server/settings/publish_plugins.py b/server_addon/aftereffects/server/settings/publish_plugins.py
index 61d67f26d3..a9f30c6686 100644
--- a/server_addon/aftereffects/server/settings/publish_plugins.py
+++ b/server_addon/aftereffects/server/settings/publish_plugins.py
@@ -22,12 +22,6 @@ class ValidateSceneSettingsModel(BaseSettingsModel):
)
-class ValidateContainersModel(BaseSettingsModel):
- enabled: bool = SettingsField(True, title="Enabled")
- optional: bool = SettingsField(True, title="Optional")
- active: bool = SettingsField(True, title="Active")
-
-
class AfterEffectsPublishPlugins(BaseSettingsModel):
CollectReview: CollectReviewPluginModel = SettingsField(
default_factory=CollectReviewPluginModel,
@@ -37,10 +31,6 @@ class AfterEffectsPublishPlugins(BaseSettingsModel):
default_factory=ValidateSceneSettingsModel,
title="Validate Scene Settings",
)
- ValidateContainers: ValidateContainersModel = SettingsField(
- default_factory=ValidateContainersModel,
- title="Validate Containers",
- )
AE_PUBLISH_PLUGINS_DEFAULTS = {
@@ -58,9 +48,4 @@ AE_PUBLISH_PLUGINS_DEFAULTS = {
".*"
]
},
- "ValidateContainers": {
- "enabled": True,
- "optional": True,
- "active": True,
- }
}
diff --git a/server_addon/applications/client/ayon_applications/utils.py b/server_addon/applications/client/ayon_applications/utils.py
index 234fa6c683..185779a949 100644
--- a/server_addon/applications/client/ayon_applications/utils.py
+++ b/server_addon/applications/client/ayon_applications/utils.py
@@ -281,13 +281,20 @@ def prepare_app_environments(
app.environment
]
+ task_entity = data.get("task_entity")
folder_entity = data.get("folder_entity")
# Add tools environments
groups_by_name = {}
tool_by_group_name = collections.defaultdict(dict)
- if folder_entity:
- # Make sure each tool group can be added only once
- for key in folder_entity["attrib"].get("tools") or []:
+ tools = None
+ if task_entity:
+ tools = task_entity["attrib"].get("tools")
+
+ if tools is None and folder_entity:
+ tools = folder_entity["attrib"].get("tools")
+
+ if tools:
+ for key in tools:
tool = app.manager.tools.get(key)
if not tool or not tool.is_valid_for_app(app):
continue
diff --git a/server_addon/applications/package.py b/server_addon/applications/package.py
index 500f609fc6..983749355e 100644
--- a/server_addon/applications/package.py
+++ b/server_addon/applications/package.py
@@ -1,6 +1,6 @@
name = "applications"
title = "Applications"
-version = "0.2.1"
+version = "0.2.2"
ayon_server_version = ">=1.0.7"
ayon_launcher_version = ">=1.0.2"
diff --git a/server_addon/applications/server/settings.py b/server_addon/applications/server/settings.py
index b77686cee0..3f9d90ef5b 100644
--- a/server_addon/applications/server/settings.py
+++ b/server_addon/applications/server/settings.py
@@ -212,7 +212,13 @@ class ApplicationsAddonSettings(BaseSettingsModel):
scope=["studio"]
)
only_available: bool = SettingsField(
- True, title="Show only available applications")
+ True,
+ title="Show only available applications",
+ description="Enable to show only applications in AYON Launcher"
+ " for which the executable paths are found on the running machine."
+ " This applies as an additional filter to the applications defined in a "
+ " project's anatomy settings to ignore unavailable applications."
+ )
@validator("tool_groups")
def validate_unique_name(cls, value):
diff --git a/server_addon/blender/package.py b/server_addon/blender/package.py
index 667076e533..d2c02a4909 100644
--- a/server_addon/blender/package.py
+++ b/server_addon/blender/package.py
@@ -1,3 +1,3 @@
name = "blender"
title = "Blender"
-version = "0.1.8"
+version = "0.1.9"
diff --git a/server_addon/blender/server/settings/publish_plugins.py b/server_addon/blender/server/settings/publish_plugins.py
index e998d7b057..8db8c5be46 100644
--- a/server_addon/blender/server/settings/publish_plugins.py
+++ b/server_addon/blender/server/settings/publish_plugins.py
@@ -151,6 +151,10 @@ class PublishPluginsModel(BaseSettingsModel):
default_factory=ExtractPlayblastModel,
title="Extract Playblast"
)
+ ExtractModelUSD: ValidatePluginModel = SettingsField(
+ default_factory=ValidatePluginModel,
+ title="Extract Model USD"
+ )
DEFAULT_BLENDER_PUBLISH_SETTINGS = {
@@ -348,5 +352,10 @@ DEFAULT_BLENDER_PUBLISH_SETTINGS = {
},
indent=4
)
+ },
+ "ExtractModelUSD": {
+ "enabled": True,
+ "optional": True,
+ "active": True
}
}
diff --git a/client/ayon_core/hosts/celaction/__init__.py b/server_addon/celaction/client/ayon_celaction/__init__.py
similarity index 71%
rename from client/ayon_core/hosts/celaction/__init__.py
rename to server_addon/celaction/client/ayon_celaction/__init__.py
index 8983d48d7d..0df0224125 100644
--- a/client/ayon_core/hosts/celaction/__init__.py
+++ b/server_addon/celaction/client/ayon_celaction/__init__.py
@@ -1,3 +1,4 @@
+from .version import __version__
from .addon import (
CELACTION_ROOT_DIR,
CelactionAddon,
@@ -5,6 +6,8 @@ from .addon import (
__all__ = (
+ "__version__",
+
"CELACTION_ROOT_DIR",
"CelactionAddon",
)
diff --git a/client/ayon_core/hosts/celaction/addon.py b/server_addon/celaction/client/ayon_celaction/addon.py
similarity index 92%
rename from client/ayon_core/hosts/celaction/addon.py
rename to server_addon/celaction/client/ayon_celaction/addon.py
index d00401a2e0..ad04a54088 100644
--- a/client/ayon_core/hosts/celaction/addon.py
+++ b/server_addon/celaction/client/ayon_celaction/addon.py
@@ -1,11 +1,14 @@
import os
from ayon_core.addon import AYONAddon, IHostAddon
+from .version import __version__
+
CELACTION_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class CelactionAddon(AYONAddon, IHostAddon):
name = "celaction"
+ version = __version__
host_name = "celaction"
def get_launch_hook_paths(self, app):
diff --git a/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py b/server_addon/celaction/client/ayon_celaction/hooks/pre_celaction_setup.py
similarity index 96%
rename from client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py
rename to server_addon/celaction/client/ayon_celaction/hooks/pre_celaction_setup.py
index 8350c7b7c8..52622d43b8 100644
--- a/client/ayon_core/hosts/celaction/hooks/pre_celaction_setup.py
+++ b/server_addon/celaction/client/ayon_celaction/hooks/pre_celaction_setup.py
@@ -4,13 +4,11 @@ import winreg
import subprocess
from ayon_core.lib import get_ayon_launcher_args
from ayon_applications import PreLaunchHook, LaunchTypes
-from ayon_core.hosts.celaction import CELACTION_ROOT_DIR
+from ayon_celaction import CELACTION_ROOT_DIR
class CelactionPrelaunchHook(PreLaunchHook):
- """
- Bootstrap celacion with pype
- """
+ """Bootstrap celacion with AYON"""
app_groups = {"celaction"}
platforms = {"windows"}
launch_types = {LaunchTypes.local}
@@ -39,7 +37,7 @@ class CelactionPrelaunchHook(PreLaunchHook):
CELACTION_ROOT_DIR, "scripts", "publish_cli.py"
)
subprocess_args = get_ayon_launcher_args("run", path_to_cli)
- openpype_executable = subprocess_args.pop(0)
+ executable = subprocess_args.pop(0)
workfile_settings = self.get_workfile_settings()
winreg.SetValueEx(
@@ -47,7 +45,7 @@ class CelactionPrelaunchHook(PreLaunchHook):
"SubmitAppTitle",
0,
winreg.REG_SZ,
- openpype_executable
+ executable
)
# add required arguments for workfile path
diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_cli_kwargs.py b/server_addon/celaction/client/ayon_celaction/plugins/publish/collect_celaction_cli_kwargs.py
similarity index 100%
rename from client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_cli_kwargs.py
rename to server_addon/celaction/client/ayon_celaction/plugins/publish/collect_celaction_cli_kwargs.py
diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py b/server_addon/celaction/client/ayon_celaction/plugins/publish/collect_celaction_instances.py
similarity index 100%
rename from client/ayon_core/hosts/celaction/plugins/publish/collect_celaction_instances.py
rename to server_addon/celaction/client/ayon_celaction/plugins/publish/collect_celaction_instances.py
diff --git a/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py b/server_addon/celaction/client/ayon_celaction/plugins/publish/collect_render_path.py
similarity index 98%
rename from client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py
rename to server_addon/celaction/client/ayon_celaction/plugins/publish/collect_render_path.py
index 1bb4d54831..3bcd1c69b3 100644
--- a/client/ayon_core/hosts/celaction/plugins/publish/collect_render_path.py
+++ b/server_addon/celaction/client/ayon_celaction/plugins/publish/collect_render_path.py
@@ -1,6 +1,6 @@
import os
-import pyblish.api
import copy
+import pyblish.api
class CollectRenderPath(pyblish.api.InstancePlugin):
@@ -10,6 +10,8 @@ class CollectRenderPath(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.495
families = ["render.farm"]
+ settings_category = "celaction"
+
# Presets
output_extension = "png"
anatomy_template_key_render_files = None
diff --git a/client/ayon_core/hosts/celaction/plugins/publish/integrate_version_up.py b/server_addon/celaction/client/ayon_celaction/plugins/publish/integrate_version_up.py
similarity index 100%
rename from client/ayon_core/hosts/celaction/plugins/publish/integrate_version_up.py
rename to server_addon/celaction/client/ayon_celaction/plugins/publish/integrate_version_up.py
diff --git a/client/ayon_core/hosts/celaction/resources/celaction_template_scene.scn b/server_addon/celaction/client/ayon_celaction/resources/celaction_template_scene.scn
similarity index 100%
rename from client/ayon_core/hosts/celaction/resources/celaction_template_scene.scn
rename to server_addon/celaction/client/ayon_celaction/resources/celaction_template_scene.scn
diff --git a/client/ayon_core/hosts/celaction/plugins/__init__.py b/server_addon/celaction/client/ayon_celaction/scripts/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/celaction/plugins/__init__.py
rename to server_addon/celaction/client/ayon_celaction/scripts/__init__.py
diff --git a/client/ayon_core/hosts/celaction/scripts/publish_cli.py b/server_addon/celaction/client/ayon_celaction/scripts/publish_cli.py
similarity index 77%
rename from client/ayon_core/hosts/celaction/scripts/publish_cli.py
rename to server_addon/celaction/client/ayon_celaction/scripts/publish_cli.py
index 92019b8702..4e54aa253a 100644
--- a/client/ayon_core/hosts/celaction/scripts/publish_cli.py
+++ b/server_addon/celaction/client/ayon_celaction/scripts/publish_cli.py
@@ -4,7 +4,7 @@ import sys
import pyblish.api
import pyblish.util
-import ayon_core.hosts.celaction
+from ayon_celaction import CELACTION_ROOT_DIR
from ayon_core.lib import Logger
from ayon_core.tools.utils import host_tools
from ayon_core.pipeline import install_ayon_plugins
@@ -13,13 +13,12 @@ from ayon_core.pipeline import install_ayon_plugins
log = Logger.get_logger("celaction")
PUBLISH_HOST = "celaction"
-HOST_DIR = os.path.dirname(os.path.abspath(ayon_core.hosts.celaction.__file__))
-PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
+PLUGINS_DIR = os.path.join(CELACTION_ROOT_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
def main():
- # Registers pype's Global pyblish plugins
+ # Registers global pyblish plugins
install_ayon_plugins()
if os.path.exists(PUBLISH_PATH):
diff --git a/server_addon/celaction/client/ayon_celaction/version.py b/server_addon/celaction/client/ayon_celaction/version.py
new file mode 100644
index 0000000000..ceed47c3a0
--- /dev/null
+++ b/server_addon/celaction/client/ayon_celaction/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring AYON addon 'celaction' version."""
+__version__ = "0.2.0"
diff --git a/server_addon/celaction/package.py b/server_addon/celaction/package.py
index 2b11a8630f..8b9069d019 100644
--- a/server_addon/celaction/package.py
+++ b/server_addon/celaction/package.py
@@ -1,3 +1,12 @@
name = "celaction"
title = "CelAction"
-version = "0.1.0"
+version = "0.2.0"
+
+client_dir = "ayon_celaction"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {
+ "applications": ">=0.2.0",
+}
diff --git a/server_addon/clockify/client/ayon_clockify/__init__.py b/server_addon/clockify/client/ayon_clockify/__init__.py
new file mode 100644
index 0000000000..75fb87494e
--- /dev/null
+++ b/server_addon/clockify/client/ayon_clockify/__init__.py
@@ -0,0 +1,5 @@
+from .addon import ClockifyAddon
+
+__all__ = (
+ "ClockifyAddon",
+)
diff --git a/client/ayon_core/modules/clockify/clockify_module.py b/server_addon/clockify/client/ayon_clockify/addon.py
similarity index 93%
rename from client/ayon_core/modules/clockify/clockify_module.py
rename to server_addon/clockify/client/ayon_clockify/addon.py
index d2ee4f1e1e..ce91b2be70 100644
--- a/client/ayon_core/modules/clockify/clockify_module.py
+++ b/server_addon/clockify/client/ayon_clockify/addon.py
@@ -2,12 +2,12 @@ import os
import threading
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
-class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
+class ClockifyAddon(AYONAddon, ITrayAddon, IPluginPaths):
name = "clockify"
def initialize(self, studio_settings):
@@ -31,7 +31,7 @@ class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
# TimersManager attributes
# - set `timers_manager_connector` only in `tray_init`
self.timers_manager_connector = None
- self._timers_manager_module = None
+ self._timer_manager_addon = None
@property
def clockify_api(self):
@@ -87,7 +87,7 @@ class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
return {"actions": [actions_path]}
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 {
"user": [CLOCKIFY_FTRACK_USER_PATH],
"server": [CLOCKIFY_FTRACK_SERVER_PATH],
@@ -206,19 +206,19 @@ class ClockifyModule(AYONAddon, ITrayModule, IPluginPaths):
self.action_stop_timer.setVisible(self.bool_timer_run)
# --- TimersManager connection methods ---
- def register_timers_manager(self, timer_manager_module):
+ def register_timers_manager(self, timer_manager_addon):
"""Store TimersManager for future use."""
- self._timers_manager_module = timer_manager_module
+ self._timer_manager_addon = timer_manager_addon
def timer_started(self, data):
"""Tell TimersManager that timer started."""
- if self._timers_manager_module is not None:
- self._timers_manager_module.timer_started(self.id, data)
+ if self._timer_manager_addon is not None:
+ self._timer_manager_addon.timer_started(self.id, data)
def timer_stopped(self):
"""Tell TimersManager that timer stopped."""
- if self._timers_manager_module is not None:
- self._timers_manager_module.timer_stopped(self.id)
+ if self._timer_manager_addon is not None:
+ self._timer_manager_addon.timer_stopped(self.id)
def stop_timer(self):
"""Called from TimersManager to stop timer."""
diff --git a/client/ayon_core/modules/clockify/clockify_api.py b/server_addon/clockify/client/ayon_clockify/clockify_api.py
similarity index 99%
rename from client/ayon_core/modules/clockify/clockify_api.py
rename to server_addon/clockify/client/ayon_clockify/clockify_api.py
index 2e1d8f008f..38ca6cdb66 100644
--- a/client/ayon_core/modules/clockify/clockify_api.py
+++ b/server_addon/clockify/client/ayon_clockify/clockify_api.py
@@ -1,15 +1,17 @@
import os
import json
import datetime
+
import requests
+
+from ayon_core.lib.local_settings import AYONSecureRegistry
+from ayon_core.lib import Logger
+
from .constants import (
CLOCKIFY_ENDPOINT,
ADMIN_PERMISSION_NAMES,
)
-from ayon_core.lib.local_settings import AYONSecureRegistry
-from ayon_core.lib import Logger
-
class ClockifyAPI:
log = Logger.get_logger(__name__)
diff --git a/client/ayon_core/modules/clockify/constants.py b/server_addon/clockify/client/ayon_clockify/constants.py
similarity index 100%
rename from client/ayon_core/modules/clockify/constants.py
rename to server_addon/clockify/client/ayon_clockify/constants.py
diff --git a/client/ayon_core/modules/clockify/ftrack/server/action_clockify_sync_server.py b/server_addon/clockify/client/ayon_clockify/ftrack/server/action_clockify_sync_server.py
similarity index 97%
rename from client/ayon_core/modules/clockify/ftrack/server/action_clockify_sync_server.py
rename to server_addon/clockify/client/ayon_clockify/ftrack/server/action_clockify_sync_server.py
index 7854f0ceba..ed83fed287 100644
--- a/client/ayon_core/modules/clockify/ftrack/server/action_clockify_sync_server.py
+++ b/server_addon/clockify/client/ayon_clockify/ftrack/server/action_clockify_sync_server.py
@@ -1,7 +1,9 @@
import os
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):
diff --git a/client/ayon_core/modules/clockify/ftrack/user/action_clockify_sync_local.py b/server_addon/clockify/client/ayon_clockify/ftrack/user/action_clockify_sync_local.py
similarity index 87%
rename from client/ayon_core/modules/clockify/ftrack/user/action_clockify_sync_local.py
rename to server_addon/clockify/client/ayon_clockify/ftrack/user/action_clockify_sync_local.py
index 4701653a0b..05a94e56fd 100644
--- a/client/ayon_core/modules/clockify/ftrack/user/action_clockify_sync_local.py
+++ b/server_addon/clockify/client/ayon_clockify/ftrack/user/action_clockify_sync_local.py
@@ -1,25 +1,20 @@
import json
-from openpype_modules.ftrack.lib import BaseAction, statics_icon
-from openpype_modules.clockify.clockify_api import ClockifyAPI
+from ayon_clockify.clockify_api import ClockifyAPI
+from ayon_ftrack.lib import BaseAction, statics_icon
class SyncClockifyLocal(BaseAction):
- '''Synchronise project names and task types.'''
+ """Synchronise project names and task types."""
- #: Action identifier.
- identifier = 'clockify.sync.local'
- #: Action label.
- label = 'Sync To Clockify (local)'
- #: Action description.
- description = 'Synchronise data to Clockify workspace'
- #: roles that are allowed to register this action
+ identifier = "clockify.sync.local"
+ label = "Sync To Clockify"
+ description = "Synchronise data to Clockify workspace"
role_list = ["Administrator", "project Manager"]
- #: icon
icon = statics_icon("app_icons", "clockify-white.png")
def __init__(self, *args, **kwargs):
super(SyncClockifyLocal, self).__init__(*args, **kwargs)
- #: CLockifyApi
+
self.clockify_api = ClockifyAPI()
def discover(self, session, entities, event):
@@ -56,7 +51,7 @@ class SyncClockifyLocal(BaseAction):
'user': user,
'status': 'running',
'data': json.dumps({
- 'description': 'Sync Ftrack to Clockify'
+ 'description': 'Sync ftrack to Clockify'
})
})
session.commit()
diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py b/server_addon/clockify/client/ayon_clockify/launcher_actions/ClockifyStart.py
similarity index 96%
rename from client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py
rename to server_addon/clockify/client/ayon_clockify/launcher_actions/ClockifyStart.py
index 8381c7d73e..d69d0371c0 100644
--- a/client/ayon_core/modules/clockify/launcher_actions/ClockifyStart.py
+++ b/server_addon/clockify/client/ayon_clockify/launcher_actions/ClockifyStart.py
@@ -1,7 +1,8 @@
import ayon_api
+from ayon_clockify.clockify_api import ClockifyAPI
+
from ayon_core.pipeline import LauncherAction
-from openpype_modules.clockify.clockify_api import ClockifyAPI
class ClockifyStart(LauncherAction):
diff --git a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py b/server_addon/clockify/client/ayon_clockify/launcher_actions/ClockifySync.py
similarity index 97%
rename from client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py
rename to server_addon/clockify/client/ayon_clockify/launcher_actions/ClockifySync.py
index 5388f47c98..a32f2a8082 100644
--- a/client/ayon_core/modules/clockify/launcher_actions/ClockifySync.py
+++ b/server_addon/clockify/client/ayon_clockify/launcher_actions/ClockifySync.py
@@ -1,6 +1,6 @@
import ayon_api
-from openpype_modules.clockify.clockify_api import ClockifyAPI
+from ayon_clockify.clockify_api import ClockifyAPI
from ayon_core.pipeline import LauncherAction
diff --git a/server_addon/clockify/client/ayon_clockify/version.py b/server_addon/clockify/client/ayon_clockify/version.py
new file mode 100644
index 0000000000..0e6e40cb7d
--- /dev/null
+++ b/server_addon/clockify/client/ayon_clockify/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring AYON addon 'clockify' version."""
+__version__ = "0.2.0"
diff --git a/client/ayon_core/modules/clockify/widgets.py b/server_addon/clockify/client/ayon_clockify/widgets.py
similarity index 100%
rename from client/ayon_core/modules/clockify/widgets.py
rename to server_addon/clockify/client/ayon_clockify/widgets.py
diff --git a/server_addon/clockify/package.py b/server_addon/clockify/package.py
index bcf9425b3f..61e0685191 100644
--- a/server_addon/clockify/package.py
+++ b/server_addon/clockify/package.py
@@ -1,3 +1,9 @@
name = "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 = {}
diff --git a/server_addon/create_ayon_addons.py b/server_addon/create_ayon_addons.py
index f0a36d4740..749077d2a8 100644
--- a/server_addon/create_ayon_addons.py
+++ b/server_addon/create_ayon_addons.py
@@ -47,7 +47,7 @@ plugin_for = ["ayon_server"]
"""
CLIENT_VERSION_CONTENT = '''# -*- coding: utf-8 -*-
-"""Package declaring AYON core addon version."""
+"""Package declaring AYON addon '{}' version."""
__version__ = "{}"
'''
@@ -183,6 +183,7 @@ def create_addon_zip(
def prepare_client_code(
+ addon_name: str,
addon_dir: Path,
addon_output_dir: Path,
addon_version: str
@@ -211,7 +212,9 @@ def prepare_client_code(
version_path = subpath / "version.py"
if version_path.exists():
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"
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
)
- 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:
create_addon_zip(
diff --git a/server_addon/deadline/package.py b/server_addon/deadline/package.py
index 25ba1c1166..e26734c813 100644
--- a/server_addon/deadline/package.py
+++ b/server_addon/deadline/package.py
@@ -1,3 +1,3 @@
name = "deadline"
title = "Deadline"
-version = "0.1.11"
+version = "0.1.12"
diff --git a/server_addon/deadline/server/__init__.py b/server_addon/deadline/server/__init__.py
index e7dcb7d347..8d2dc152cd 100644
--- a/server_addon/deadline/server/__init__.py
+++ b/server_addon/deadline/server/__init__.py
@@ -2,11 +2,13 @@ from typing import Type
from ayon_server.addons import BaseServerAddon
-from .settings import DeadlineSettings, DEFAULT_VALUES
+from .settings import DeadlineSettings, DEFAULT_VALUES, DeadlineSiteSettings
class Deadline(BaseServerAddon):
settings_model: Type[DeadlineSettings] = DeadlineSettings
+ site_settings_model: Type[DeadlineSiteSettings] = DeadlineSiteSettings
+
async def get_default_settings(self):
settings_model_cls = self.get_settings_model()
diff --git a/server_addon/deadline/server/settings/__init__.py b/server_addon/deadline/server/settings/__init__.py
index 0307862afa..d25c0fb330 100644
--- a/server_addon/deadline/server/settings/__init__.py
+++ b/server_addon/deadline/server/settings/__init__.py
@@ -2,9 +2,11 @@ from .main import (
DeadlineSettings,
DEFAULT_VALUES,
)
+from .site_settings import DeadlineSiteSettings
__all__ = (
"DeadlineSettings",
+ "DeadlineSiteSettings",
"DEFAULT_VALUES",
)
diff --git a/server_addon/deadline/server/settings/main.py b/server_addon/deadline/server/settings/main.py
index 21a314cd2f..47ad72a86f 100644
--- a/server_addon/deadline/server/settings/main.py
+++ b/server_addon/deadline/server/settings/main.py
@@ -15,12 +15,6 @@ from .publish_plugins import (
)
-class ServerListSubmodel(BaseSettingsModel):
- _layout = "compact"
- name: str = SettingsField(title="Name")
- value: str = SettingsField(title="Value")
-
-
async def defined_deadline_ws_name_enum_resolver(
addon: "BaseServerAddon",
settings_variant: str = "production",
@@ -32,25 +26,39 @@ async def defined_deadline_ws_name_enum_resolver(
settings = await addon.get_studio_settings(variant=settings_variant)
- ws_urls = []
+ ws_server_name = []
for deadline_url_item in settings.deadline_urls:
- ws_urls.append(deadline_url_item.name)
+ ws_server_name.append(deadline_url_item.name)
- return ws_urls
+ return ws_server_name
+
+class ServerItemSubmodel(BaseSettingsModel):
+ """Connection info about configured DL servers."""
+ _layout = "compact"
+ name: str = SettingsField(title="Name")
+ value: str = SettingsField(title="Url")
+ require_authentication: bool = SettingsField(
+ False, title="Require authentication")
+ not_verify_ssl: bool = SettingsField(
+ False, title="Don't verify SSL")
class DeadlineSettings(BaseSettingsModel):
- deadline_urls: list[ServerListSubmodel] = SettingsField(
+ # configured DL servers
+ deadline_urls: list[ServerItemSubmodel] = SettingsField(
default_factory=list,
- title="System Deadline Webservice URLs",
+ title="System Deadline Webservice Info",
scope=["studio"],
)
+
+ # name(key) of selected server for project
deadline_server: str = SettingsField(
- title="Project deadline server",
+ title="Project Deadline server name",
section="---",
scope=["project"],
enum_resolver=defined_deadline_ws_name_enum_resolver
)
+
publish: PublishPluginsModel = SettingsField(
default_factory=PublishPluginsModel,
title="Publish Plugins",
@@ -62,11 +70,14 @@ class DeadlineSettings(BaseSettingsModel):
return value
+
DEFAULT_VALUES = {
"deadline_urls": [
{
"name": "default",
- "value": "http://127.0.0.1:8082"
+ "value": "http://127.0.0.1:8082",
+ "require_authentication": False,
+ "not_verify_ssl": False
}
],
"deadline_server": "default",
diff --git a/server_addon/deadline/server/settings/site_settings.py b/server_addon/deadline/server/settings/site_settings.py
new file mode 100644
index 0000000000..a77a6edc7e
--- /dev/null
+++ b/server_addon/deadline/server/settings/site_settings.py
@@ -0,0 +1,26 @@
+from ayon_server.settings import (
+ BaseSettingsModel,
+ SettingsField,
+)
+from .main import defined_deadline_ws_name_enum_resolver
+
+
+class CredentialPerServerModel(BaseSettingsModel):
+ """Provide credentials for configured DL servers"""
+ _layout = "expanded"
+ server_name: str = SettingsField("",
+ title="DL server name",
+ enum_resolver=defined_deadline_ws_name_enum_resolver)
+ username: str = SettingsField("",
+ title="Username")
+ password: str = SettingsField("",
+ title="Password")
+
+
+class DeadlineSiteSettings(BaseSettingsModel):
+ local_settings: list[CredentialPerServerModel] = SettingsField(
+ default_factory=list,
+ title="Local setting",
+ description="Please provide credentials for configured Deadline servers",
+ )
+
diff --git a/server_addon/flame/client/ayon_flame/__init__.py b/server_addon/flame/client/ayon_flame/__init__.py
new file mode 100644
index 0000000000..d2d89bdb01
--- /dev/null
+++ b/server_addon/flame/client/ayon_flame/__init__.py
@@ -0,0 +1,13 @@
+from .version import __version__
+from .addon import (
+ FLAME_ADDON_ROOT,
+ FlameAddon,
+)
+
+
+__all__ = (
+ "__version__",
+
+ "FLAME_ADDON_ROOT",
+ "FlameAddon",
+)
diff --git a/client/ayon_core/hosts/flame/addon.py b/server_addon/flame/client/ayon_flame/addon.py
similarity index 74%
rename from client/ayon_core/hosts/flame/addon.py
rename to server_addon/flame/client/ayon_flame/addon.py
index f5560cde7a..5a96a9332e 100644
--- a/client/ayon_core/hosts/flame/addon.py
+++ b/server_addon/flame/client/ayon_flame/addon.py
@@ -1,16 +1,19 @@
import os
from ayon_core.addon import AYONAddon, IHostAddon
-HOST_DIR = os.path.dirname(os.path.abspath(__file__))
+from .version import __version__
+
+FLAME_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
class FlameAddon(AYONAddon, IHostAddon):
name = "flame"
+ version = __version__
host_name = "flame"
def add_implementation_envs(self, env, _app):
# Add requirements to DL_PYTHON_HOOK_PATH
- env["DL_PYTHON_HOOK_PATH"] = os.path.join(HOST_DIR, "startup")
+ env["DL_PYTHON_HOOK_PATH"] = os.path.join(FLAME_ADDON_ROOT, "startup")
env.pop("QT_AUTO_SCREEN_SCALE_FACTOR", None)
# Set default values if are not already set via settings
@@ -25,7 +28,7 @@ class FlameAddon(AYONAddon, IHostAddon):
if app.host_name != self.host_name:
return []
return [
- os.path.join(HOST_DIR, "hooks")
+ os.path.join(FLAME_ADDON_ROOT, "hooks")
]
def get_workfile_extensions(self):
diff --git a/client/ayon_core/hosts/flame/api/__init__.py b/server_addon/flame/client/ayon_flame/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/__init__.py
rename to server_addon/flame/client/ayon_flame/api/__init__.py
diff --git a/client/ayon_core/hosts/flame/api/batch_utils.py b/server_addon/flame/client/ayon_flame/api/batch_utils.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/batch_utils.py
rename to server_addon/flame/client/ayon_flame/api/batch_utils.py
diff --git a/client/ayon_core/hosts/flame/api/constants.py b/server_addon/flame/client/ayon_flame/api/constants.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/constants.py
rename to server_addon/flame/client/ayon_flame/api/constants.py
diff --git a/client/ayon_core/hosts/flame/api/lib.py b/server_addon/flame/client/ayon_flame/api/lib.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/lib.py
rename to server_addon/flame/client/ayon_flame/api/lib.py
diff --git a/client/ayon_core/hosts/flame/api/menu.py b/server_addon/flame/client/ayon_flame/api/menu.py
similarity index 99%
rename from client/ayon_core/hosts/flame/api/menu.py
rename to server_addon/flame/client/ayon_flame/api/menu.py
index 7e880483f5..83d75d18d3 100644
--- a/client/ayon_core/hosts/flame/api/menu.py
+++ b/server_addon/flame/client/ayon_flame/api/menu.py
@@ -28,7 +28,7 @@ default_flame_export_presets = {
def callback_selection(selection, function):
- import ayon_core.hosts.flame.api as opfapi
+ import ayon_flame.api as opfapi
opfapi.CTX.selection = selection
print("Hook Selection: \n\t{}".format(
pformat({
diff --git a/client/ayon_core/hosts/flame/api/pipeline.py b/server_addon/flame/client/ayon_flame/api/pipeline.py
similarity index 95%
rename from client/ayon_core/hosts/flame/api/pipeline.py
rename to server_addon/flame/client/ayon_flame/api/pipeline.py
index 4578d7bb4b..121b925920 100644
--- a/client/ayon_core/hosts/flame/api/pipeline.py
+++ b/server_addon/flame/client/ayon_flame/api/pipeline.py
@@ -13,6 +13,7 @@ from ayon_core.pipeline import (
deregister_creator_plugin_path,
AVALON_CONTAINER_ID,
)
+from ayon_flame import FLAME_ADDON_ROOT
from .lib import (
set_segment_data_marker,
set_publish_attribute,
@@ -20,10 +21,8 @@ from .lib import (
get_current_sequence,
reset_segment_selection
)
-from .. import HOST_DIR
-API_DIR = os.path.join(HOST_DIR, "api")
-PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
+PLUGINS_DIR = os.path.join(FLAME_ADDON_ROOT, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
@@ -113,10 +112,6 @@ def on_pyblish_instance_toggled(instance, old_value, new_value):
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
instance, old_value, new_value))
- # from ayon_core.hosts.resolve import (
- # set_publish_attribute
- # )
-
# # Whether instances should be passthrough based on new value
# timeline_item = instance.data["item"]
# set_publish_attribute(timeline_item, new_value)
diff --git a/client/ayon_core/hosts/flame/api/plugin.py b/server_addon/flame/client/ayon_flame/api/plugin.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/plugin.py
rename to server_addon/flame/client/ayon_flame/api/plugin.py
diff --git a/client/ayon_core/hosts/flame/api/render_utils.py b/server_addon/flame/client/ayon_flame/api/render_utils.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/render_utils.py
rename to server_addon/flame/client/ayon_flame/api/render_utils.py
diff --git a/client/ayon_core/hosts/flame/api/scripts/wiretap_com.py b/server_addon/flame/client/ayon_flame/api/scripts/wiretap_com.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/scripts/wiretap_com.py
rename to server_addon/flame/client/ayon_flame/api/scripts/wiretap_com.py
diff --git a/client/ayon_core/hosts/flame/api/utils.py b/server_addon/flame/client/ayon_flame/api/utils.py
similarity index 98%
rename from client/ayon_core/hosts/flame/api/utils.py
rename to server_addon/flame/client/ayon_flame/api/utils.py
index b76dd92ada..03a694c25c 100644
--- a/client/ayon_core/hosts/flame/api/utils.py
+++ b/server_addon/flame/client/ayon_flame/api/utils.py
@@ -5,6 +5,8 @@ Flame utils for syncing scripts
import os
import shutil
from ayon_core.lib import Logger
+from ayon_flame import FLAME_ADDON_ROOT
+
log = Logger.get_logger(__name__)
@@ -16,7 +18,6 @@ def _sync_utility_scripts(env=None):
`/opt/Autodesk/shared/python`. This will be always synchronizing those
folders.
"""
- from .. import HOST_DIR
env = env or os.environ
@@ -26,7 +27,7 @@ def _sync_utility_scripts(env=None):
flame_shared_dir = "/opt/Autodesk/shared/python"
fsd_paths = [os.path.join(
- HOST_DIR,
+ FLAME_ADDON_ROOT,
"api",
"utility_scripts"
)]
diff --git a/client/ayon_core/hosts/flame/api/workio.py b/server_addon/flame/client/ayon_flame/api/workio.py
similarity index 100%
rename from client/ayon_core/hosts/flame/api/workio.py
rename to server_addon/flame/client/ayon_flame/api/workio.py
diff --git a/client/ayon_core/hosts/flame/hooks/pre_flame_setup.py b/server_addon/flame/client/ayon_flame/hooks/pre_flame_setup.py
similarity index 98%
rename from client/ayon_core/hosts/flame/hooks/pre_flame_setup.py
rename to server_addon/flame/client/ayon_flame/hooks/pre_flame_setup.py
index 77a9435205..e9e9aca3f4 100644
--- a/client/ayon_core/hosts/flame/hooks/pre_flame_setup.py
+++ b/server_addon/flame/client/ayon_flame/hooks/pre_flame_setup.py
@@ -10,7 +10,7 @@ from ayon_core.lib import (
run_subprocess,
)
from ayon_applications import PreLaunchHook, LaunchTypes
-from ayon_core.hosts import flame as opflame
+from ayon_flame import FLAME_ADDON_ROOT
class FlamePrelaunch(PreLaunchHook):
@@ -23,7 +23,8 @@ class FlamePrelaunch(PreLaunchHook):
permissions = 0o777
wtc_script_path = os.path.join(
- opflame.HOST_DIR, "api", "scripts", "wiretap_com.py")
+ FLAME_ADDON_ROOT, "api", "scripts", "wiretap_com.py"
+ )
launch_types = {LaunchTypes.local}
def __init__(self, *args, **kwargs):
diff --git a/client/ayon_core/hosts/celaction/scripts/__init__.py b/server_addon/flame/client/ayon_flame/otio/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/celaction/scripts/__init__.py
rename to server_addon/flame/client/ayon_flame/otio/__init__.py
diff --git a/client/ayon_core/hosts/flame/otio/flame_export.py b/server_addon/flame/client/ayon_flame/otio/flame_export.py
similarity index 99%
rename from client/ayon_core/hosts/flame/otio/flame_export.py
rename to server_addon/flame/client/ayon_flame/otio/flame_export.py
index cb038f9e9a..bebe9be1c1 100644
--- a/client/ayon_core/hosts/flame/otio/flame_export.py
+++ b/server_addon/flame/client/ayon_flame/otio/flame_export.py
@@ -275,7 +275,7 @@ def create_otio_reference(clip_data, fps=None):
def create_otio_clip(clip_data):
- from ayon_core.hosts.flame.api import MediaInfoFile, TimeEffectMetadata
+ from ayon_flame.api import MediaInfoFile, TimeEffectMetadata
segment = clip_data["PySegment"]
diff --git a/client/ayon_core/hosts/flame/otio/utils.py b/server_addon/flame/client/ayon_flame/otio/utils.py
similarity index 100%
rename from client/ayon_core/hosts/flame/otio/utils.py
rename to server_addon/flame/client/ayon_flame/otio/utils.py
diff --git a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py b/server_addon/flame/client/ayon_flame/plugins/create/create_shot_clip.py
similarity index 99%
rename from client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py
rename to server_addon/flame/client/ayon_flame/plugins/create/create_shot_clip.py
index 56f5319f21..120c8c559d 100644
--- a/client/ayon_core/hosts/flame/plugins/create/create_shot_clip.py
+++ b/server_addon/flame/client/ayon_flame/plugins/create/create_shot_clip.py
@@ -1,5 +1,5 @@
from copy import deepcopy
-import ayon_core.hosts.flame.api as opfapi
+import ayon_flame.api as opfapi
class CreateShotClip(opfapi.Creator):
diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip.py b/server_addon/flame/client/ayon_flame/plugins/load/load_clip.py
similarity index 99%
rename from client/ayon_core/hosts/flame/plugins/load/load_clip.py
rename to server_addon/flame/client/ayon_flame/plugins/load/load_clip.py
index 40ab9c038b..c8ec7b36c9 100644
--- a/client/ayon_core/hosts/flame/plugins/load/load_clip.py
+++ b/server_addon/flame/client/ayon_flame/plugins/load/load_clip.py
@@ -2,7 +2,7 @@ from copy import deepcopy
import os
import flame
from pprint import pformat
-import ayon_core.hosts.flame.api as opfapi
+import ayon_flame.api as opfapi
from ayon_core.lib import StringTemplate
from ayon_core.lib.transcoding import (
VIDEO_EXTENSIONS,
diff --git a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py b/server_addon/flame/client/ayon_flame/plugins/load/load_clip_batch.py
similarity index 99%
rename from client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py
rename to server_addon/flame/client/ayon_flame/plugins/load/load_clip_batch.py
index 1b23a8b465..0d7a125af7 100644
--- a/client/ayon_core/hosts/flame/plugins/load/load_clip_batch.py
+++ b/server_addon/flame/client/ayon_flame/plugins/load/load_clip_batch.py
@@ -2,7 +2,7 @@ from copy import deepcopy
import os
import flame
from pprint import pformat
-import ayon_core.hosts.flame.api as opfapi
+import ayon_flame.api as opfapi
from ayon_core.lib import StringTemplate
from ayon_core.lib.transcoding import (
VIDEO_EXTENSIONS,
diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_test_selection.py b/server_addon/flame/client/ayon_flame/plugins/publish/collect_test_selection.py
similarity index 94%
rename from client/ayon_core/hosts/flame/plugins/publish/collect_test_selection.py
rename to server_addon/flame/client/ayon_flame/plugins/publish/collect_test_selection.py
index 7442e7df48..dac2c862e6 100644
--- a/client/ayon_core/hosts/flame/plugins/publish/collect_test_selection.py
+++ b/server_addon/flame/client/ayon_flame/plugins/publish/collect_test_selection.py
@@ -1,8 +1,8 @@
import os
import pyblish.api
import tempfile
-import ayon_core.hosts.flame.api as opfapi
-from ayon_core.hosts.flame.otio import flame_export as otio_export
+import ayon_flame.api as opfapi
+from ayon_flame.otio import flame_export as otio_export
import opentimelineio as otio
from pprint import pformat
reload(otio_export) # noqa
diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py b/server_addon/flame/client/ayon_flame/plugins/publish/collect_timeline_instances.py
similarity index 99%
rename from client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py
rename to server_addon/flame/client/ayon_flame/plugins/publish/collect_timeline_instances.py
index ca5475824d..35591f1a0d 100644
--- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_instances.py
+++ b/server_addon/flame/client/ayon_flame/plugins/publish/collect_timeline_instances.py
@@ -1,8 +1,8 @@
import re
from types import NoneType
import pyblish
-import ayon_core.hosts.flame.api as opfapi
-from ayon_core.hosts.flame.otio import flame_export
+import ayon_flame.api as opfapi
+from ayon_flame.otio import flame_export
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
from ayon_core.pipeline.editorial import (
is_overlapping_otio_ranges,
@@ -24,6 +24,8 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
label = "Collect timeline Instances"
hosts = ["flame"]
+ settings_category = "flame"
+
audio_track_items = []
# settings
diff --git a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py b/server_addon/flame/client/ayon_flame/plugins/publish/collect_timeline_otio.py
similarity index 95%
rename from client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py
rename to server_addon/flame/client/ayon_flame/plugins/publish/collect_timeline_otio.py
index 7609ea7879..139ac5b875 100644
--- a/client/ayon_core/hosts/flame/plugins/publish/collect_timeline_otio.py
+++ b/server_addon/flame/client/ayon_flame/plugins/publish/collect_timeline_otio.py
@@ -1,7 +1,7 @@
import pyblish.api
-import ayon_core.hosts.flame.api as opfapi
-from ayon_core.hosts.flame.otio import flame_export
+import ayon_flame.api as opfapi
+from ayon_flame.otio import flame_export
from ayon_core.pipeline.create import get_product_name
diff --git a/client/ayon_core/hosts/flame/plugins/publish/extract_otio_file.py b/server_addon/flame/client/ayon_flame/plugins/publish/extract_otio_file.py
similarity index 100%
rename from client/ayon_core/hosts/flame/plugins/publish/extract_otio_file.py
rename to server_addon/flame/client/ayon_flame/plugins/publish/extract_otio_file.py
diff --git a/client/ayon_core/hosts/flame/plugins/publish/extract_subset_resources.py b/server_addon/flame/client/ayon_flame/plugins/publish/extract_subset_resources.py
similarity index 99%
rename from client/ayon_core/hosts/flame/plugins/publish/extract_subset_resources.py
rename to server_addon/flame/client/ayon_flame/plugins/publish/extract_subset_resources.py
index cae08cd76b..66c6181ffb 100644
--- a/client/ayon_core/hosts/flame/plugins/publish/extract_subset_resources.py
+++ b/server_addon/flame/client/ayon_flame/plugins/publish/extract_subset_resources.py
@@ -5,8 +5,8 @@ from copy import deepcopy
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.flame import api as opfapi
-from ayon_core.hosts.flame.api import MediaInfoFile
+from ayon_flame import api as opfapi
+from ayon_flame.api import MediaInfoFile
from ayon_core.pipeline.editorial import (
get_media_range_with_retimes
)
@@ -24,6 +24,8 @@ class ExtractProductResources(publish.Extractor):
families = ["clip"]
hosts = ["flame"]
+ settings_category = "flame"
+
# plugin defaults
keep_original_representation = False
diff --git a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py b/server_addon/flame/client/ayon_flame/plugins/publish/integrate_batch_group.py
similarity index 99%
rename from client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py
rename to server_addon/flame/client/ayon_flame/plugins/publish/integrate_batch_group.py
index d8669f836d..f77c9e9116 100644
--- a/client/ayon_core/hosts/flame/plugins/publish/integrate_batch_group.py
+++ b/server_addon/flame/client/ayon_flame/plugins/publish/integrate_batch_group.py
@@ -3,7 +3,7 @@ import copy
from collections import OrderedDict
from pprint import pformat
import pyblish
-import ayon_core.hosts.flame.api as opfapi
+import ayon_flame.api as opfapi
import ayon_core.pipeline as op_pipeline
from ayon_core.pipeline.workfile import get_workdir
@@ -16,6 +16,8 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin):
hosts = ["flame"]
families = ["clip"]
+ settings_category = "flame"
+
# settings
default_loader = "LoadClip"
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/export_preset/openpype_seg_thumbnails_jpg.xml b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/export_preset/openpype_seg_thumbnails_jpg.xml
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/export_preset/openpype_seg_thumbnails_jpg.xml
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/export_preset/openpype_seg_thumbnails_jpg.xml
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/export_preset/openpype_seg_video_h264.xml b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/export_preset/openpype_seg_video_h264.xml
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/export_preset/openpype_seg_video_h264.xml
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/export_preset/openpype_seg_video_h264.xml
diff --git a/client/ayon_core/hosts/flame/otio/__init__.py b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/flame/otio/__init__.py
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/__init__.py
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/app_utils.py b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/app_utils.py
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/app_utils.py
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/app_utils.py
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/ftrack_lib.py b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/ftrack_lib.py
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/ftrack_lib.py
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/ftrack_lib.py
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/panel_app.py b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/panel_app.py
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/panel_app.py
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/panel_app.py
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/uiwidgets.py b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/uiwidgets.py
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/uiwidgets.py
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/modules/uiwidgets.py
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/openpype_babypublisher.py b/server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/openpype_babypublisher.py
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/openpype_babypublisher.py
rename to server_addon/flame/client/ayon_flame/startup/openpype_babypublisher/openpype_babypublisher.py
diff --git a/client/ayon_core/hosts/flame/startup/openpype_in_flame.py b/server_addon/flame/client/ayon_flame/startup/openpype_in_flame.py
similarity index 99%
rename from client/ayon_core/hosts/flame/startup/openpype_in_flame.py
rename to server_addon/flame/client/ayon_flame/startup/openpype_in_flame.py
index b9cbf9700b..8f319a88eb 100644
--- a/client/ayon_core/hosts/flame/startup/openpype_in_flame.py
+++ b/server_addon/flame/client/ayon_flame/startup/openpype_in_flame.py
@@ -4,7 +4,7 @@ from qtpy import QtWidgets
from pprint import pformat
import atexit
-import ayon_core.hosts.flame.api as opfapi
+import ayon_flame.api as opfapi
from ayon_core.pipeline import (
install_host,
registered_host,
diff --git a/server_addon/flame/client/ayon_flame/version.py b/server_addon/flame/client/ayon_flame/version.py
new file mode 100644
index 0000000000..0004797e59
--- /dev/null
+++ b/server_addon/flame/client/ayon_flame/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring AYON addon 'flame' version."""
+__version__ = "0.2.0"
diff --git a/server_addon/flame/package.py b/server_addon/flame/package.py
index 8c077ed91d..f228e61f8e 100644
--- a/server_addon/flame/package.py
+++ b/server_addon/flame/package.py
@@ -1,3 +1,10 @@
name = "flame"
title = "Flame"
-version = "0.1.0"
+version = "0.2.0"
+
+client_dir = "ayon_flame"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/client/ayon_core/hosts/fusion/__init__.py b/server_addon/fusion/client/ayon_fusion/__init__.py
similarity index 63%
rename from client/ayon_core/hosts/fusion/__init__.py
rename to server_addon/fusion/client/ayon_fusion/__init__.py
index 1da11ba9d1..f2ddccdd87 100644
--- a/client/ayon_core/hosts/fusion/__init__.py
+++ b/server_addon/fusion/client/ayon_fusion/__init__.py
@@ -1,14 +1,17 @@
+from .version import __version__
from .addon import (
get_fusion_version,
FusionAddon,
- FUSION_HOST_DIR,
+ FUSION_ADDON_ROOT,
FUSION_VERSIONS_DICT,
)
__all__ = (
+ "__version__",
+
"get_fusion_version",
"FusionAddon",
- "FUSION_HOST_DIR",
+ "FUSION_ADDON_ROOT",
"FUSION_VERSIONS_DICT",
)
diff --git a/client/ayon_core/hosts/fusion/addon.py b/server_addon/fusion/client/ayon_fusion/addon.py
similarity index 91%
rename from client/ayon_core/hosts/fusion/addon.py
rename to server_addon/fusion/client/ayon_fusion/addon.py
index 54e48ea7bf..ffc70b6ff4 100644
--- a/client/ayon_core/hosts/fusion/addon.py
+++ b/server_addon/fusion/client/ayon_fusion/addon.py
@@ -3,7 +3,9 @@ import re
from ayon_core.addon import AYONAddon, IHostAddon
from ayon_core.lib import Logger
-FUSION_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
+from .version import __version__
+
+FUSION_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
# FUSION_VERSIONS_DICT is used by the pre-launch hooks
# The keys correspond to all currently supported Fusion versions
@@ -50,12 +52,13 @@ def get_fusion_version(app_name):
class FusionAddon(AYONAddon, IHostAddon):
name = "fusion"
+ version = __version__
host_name = "fusion"
def get_launch_hook_paths(self, app):
if app.host_name != self.host_name:
return []
- return [os.path.join(FUSION_HOST_DIR, "hooks")]
+ return [os.path.join(FUSION_ADDON_ROOT, "hooks")]
def add_implementation_envs(self, env, app):
# Set default values if are not already set via settings
diff --git a/client/ayon_core/hosts/fusion/api/__init__.py b/server_addon/fusion/client/ayon_fusion/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/api/__init__.py
rename to server_addon/fusion/client/ayon_fusion/api/__init__.py
diff --git a/client/ayon_core/hosts/fusion/api/action.py b/server_addon/fusion/client/ayon_fusion/api/action.py
similarity index 53%
rename from client/ayon_core/hosts/fusion/api/action.py
rename to server_addon/fusion/client/ayon_fusion/api/action.py
index 1643f1ce03..02cd96f56c 100644
--- a/client/ayon_core/hosts/fusion/api/action.py
+++ b/server_addon/fusion/client/ayon_fusion/api/action.py
@@ -1,7 +1,7 @@
import pyblish.api
-from ayon_core.hosts.fusion.api.lib import get_current_comp
+from ayon_fusion.api.lib import get_current_comp
from ayon_core.pipeline.publish import get_errored_instances_from_context
@@ -58,3 +58,55 @@ class SelectInvalidAction(pyblish.api.Action):
self.log.info(
"Selecting invalid tools: %s" % ", ".join(sorted(names))
)
+
+
+class SelectToolAction(pyblish.api.Action):
+ """Select invalid output tool in Fusion when plug-in failed.
+
+ """
+
+ label = "Select saver"
+ on = "failed" # This action is only available on a failed plug-in
+ icon = "search" # Icon from Awesome Icon
+
+ def process(self, context, plugin):
+ errored_instances = get_errored_instances_from_context(
+ context,
+ plugin=plugin,
+ )
+
+ # Get the invalid nodes for the plug-ins
+ self.log.info("Finding invalid nodes..")
+ tools = []
+ for instance in errored_instances:
+
+ tool = instance.data.get("tool")
+ if tool is not None:
+ tools.append(tool)
+ else:
+ self.log.warning(
+ "Plug-in returned to be invalid, "
+ f"but has no saver for instance {instance.name}."
+ )
+
+ if not tools:
+ # Assume relevant comp is current comp and clear selection
+ self.log.info("No invalid tools found.")
+ comp = get_current_comp()
+ flow = comp.CurrentFrame.FlowView
+ flow.Select() # No args equals clearing selection
+ return
+
+ # Assume a single comp
+ first_tool = tools[0]
+ comp = first_tool.Comp()
+ flow = comp.CurrentFrame.FlowView
+ flow.Select() # No args equals clearing selection
+ names = set()
+ for tool in tools:
+ flow.Select(tool, True)
+ comp.SetActiveTool(tool)
+ names.add(tool.Name)
+ self.log.info(
+ "Selecting invalid tools: %s" % ", ".join(sorted(names))
+ )
diff --git a/client/ayon_core/hosts/fusion/api/lib.py b/server_addon/fusion/client/ayon_fusion/api/lib.py
similarity index 99%
rename from client/ayon_core/hosts/fusion/api/lib.py
rename to server_addon/fusion/client/ayon_fusion/api/lib.py
index 08722463e1..7f7d20010d 100644
--- a/client/ayon_core/hosts/fusion/api/lib.py
+++ b/server_addon/fusion/client/ayon_fusion/api/lib.py
@@ -169,7 +169,7 @@ def validate_comp_prefs(comp=None, force_repair=False):
def _on_repair():
attributes = dict()
for key, comp_key, _label in validations:
- value = folder_value[key]
+ value = folder_attributes[key]
comp_key_full = "Comp.FrameFormat.{}".format(comp_key)
attributes[comp_key_full] = value
comp.SetPrefs(attributes)
diff --git a/client/ayon_core/hosts/fusion/api/menu.py b/server_addon/fusion/client/ayon_fusion/api/menu.py
similarity index 98%
rename from client/ayon_core/hosts/fusion/api/menu.py
rename to server_addon/fusion/client/ayon_fusion/api/menu.py
index 6a64ad2120..38d8c36bb1 100644
--- a/client/ayon_core/hosts/fusion/api/menu.py
+++ b/server_addon/fusion/client/ayon_fusion/api/menu.py
@@ -6,10 +6,10 @@ from qtpy import QtWidgets, QtCore, QtGui
from ayon_core.tools.utils import host_tools
from ayon_core.style import load_stylesheet
from ayon_core.lib import register_event_callback
-from ayon_core.hosts.fusion.scripts import (
+from ayon_fusion.scripts import (
duplicate_with_inputs,
)
-from ayon_core.hosts.fusion.api.lib import (
+from ayon_fusion.api.lib import (
set_current_context_framerange,
set_current_context_resolution,
)
diff --git a/client/ayon_core/hosts/fusion/api/pipeline.py b/server_addon/fusion/client/ayon_fusion/api/pipeline.py
similarity index 98%
rename from client/ayon_core/hosts/fusion/api/pipeline.py
rename to server_addon/fusion/client/ayon_fusion/api/pipeline.py
index 2d1073ec7d..04f0d3db9a 100644
--- a/client/ayon_core/hosts/fusion/api/pipeline.py
+++ b/server_addon/fusion/client/ayon_fusion/api/pipeline.py
@@ -22,9 +22,9 @@ from ayon_core.pipeline import (
AVALON_CONTAINER_ID,
)
from ayon_core.pipeline.load import any_outdated_containers
-from ayon_core.hosts.fusion import FUSION_HOST_DIR
from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost
from ayon_core.tools.utils import host_tools
+from ayon_fusion import FUSION_ADDON_ROOT
from .lib import (
@@ -35,7 +35,7 @@ from .lib import (
log = Logger.get_logger(__name__)
-PLUGINS_DIR = os.path.join(FUSION_HOST_DIR, "plugins")
+PLUGINS_DIR = os.path.join(FUSION_ADDON_ROOT, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
@@ -80,7 +80,7 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
and loaders into fusion.
It is called automatically when installing via
- `ayon_core.pipeline.install_host(ayon_core.hosts.fusion.api)`
+ `ayon_core.pipeline.install_host(ayon_fusion.api)`
See the Maya equivalent for inspiration on how to implement this.
@@ -127,7 +127,7 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
def open_workfile(self, filepath):
# Hack to get fusion, see
- # ayon_core.hosts.fusion.api.pipeline.get_current_comp()
+ # ayon_fusion.api.pipeline.get_current_comp()
fusion = getattr(sys.modules["__main__"], "fusion", None)
return fusion.LoadComp(filepath)
diff --git a/client/ayon_core/hosts/fusion/api/plugin.py b/server_addon/fusion/client/ayon_fusion/api/plugin.py
similarity index 99%
rename from client/ayon_core/hosts/fusion/api/plugin.py
rename to server_addon/fusion/client/ayon_fusion/api/plugin.py
index efe8269120..48e133cc63 100644
--- a/client/ayon_core/hosts/fusion/api/plugin.py
+++ b/server_addon/fusion/client/ayon_fusion/api/plugin.py
@@ -1,7 +1,7 @@
from copy import deepcopy
import os
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
get_current_comp,
comp_lock_and_undo_chunk,
)
diff --git a/client/ayon_core/hosts/fusion/api/pulse.py b/server_addon/fusion/client/ayon_fusion/api/pulse.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/api/pulse.py
rename to server_addon/fusion/client/ayon_fusion/api/pulse.py
diff --git a/client/ayon_core/hosts/fusion/deploy/MenuScripts/README.md b/server_addon/fusion/client/ayon_fusion/deploy/MenuScripts/README.md
similarity index 100%
rename from client/ayon_core/hosts/fusion/deploy/MenuScripts/README.md
rename to server_addon/fusion/client/ayon_fusion/deploy/MenuScripts/README.md
diff --git a/client/ayon_core/hosts/fusion/deploy/MenuScripts/install_pyside2.py b/server_addon/fusion/client/ayon_fusion/deploy/MenuScripts/install_pyside2.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/deploy/MenuScripts/install_pyside2.py
rename to server_addon/fusion/client/ayon_fusion/deploy/MenuScripts/install_pyside2.py
diff --git a/client/ayon_core/hosts/fusion/deploy/MenuScripts/launch_menu.py b/server_addon/fusion/client/ayon_fusion/deploy/MenuScripts/launch_menu.py
similarity index 81%
rename from client/ayon_core/hosts/fusion/deploy/MenuScripts/launch_menu.py
rename to server_addon/fusion/client/ayon_fusion/deploy/MenuScripts/launch_menu.py
index 640f78eeb8..0c5010f6a7 100644
--- a/client/ayon_core/hosts/fusion/deploy/MenuScripts/launch_menu.py
+++ b/server_addon/fusion/client/ayon_fusion/deploy/MenuScripts/launch_menu.py
@@ -5,10 +5,9 @@ if sys.version_info < (3, 7):
# hack to handle discrepancy between distributed libraries and Python 3.6
# mostly because wrong version of urllib3
# TODO remove when not necessary
- from ayon_core import AYON_CORE_ROOT
- FUSION_HOST_DIR = os.path.join(AYON_CORE_ROOT, "hosts", "fusion")
+ from ayon_fusion import FUSION_ADDON_ROOT
- vendor_path = os.path.join(FUSION_HOST_DIR, "vendor")
+ vendor_path = os.path.join(FUSION_ADDON_ROOT, "vendor")
if vendor_path not in sys.path:
sys.path.insert(0, vendor_path)
@@ -26,8 +25,8 @@ def main(env):
# However the contents of that folder can conflict with Qt library dlls
# so we make sure to move out of it to avoid DLL Load Failed errors.
os.chdir("..")
- from ayon_core.hosts.fusion.api import FusionHost
- from ayon_core.hosts.fusion.api import menu
+ from ayon_fusion.api import FusionHost
+ from ayon_fusion.api import menu
# activate resolve from pype
install_host(FusionHost())
diff --git a/client/ayon_core/hosts/fusion/deploy/ayon/Config/menu.fu b/server_addon/fusion/client/ayon_fusion/deploy/ayon/Config/menu.fu
similarity index 100%
rename from client/ayon_core/hosts/fusion/deploy/ayon/Config/menu.fu
rename to server_addon/fusion/client/ayon_fusion/deploy/ayon/Config/menu.fu
diff --git a/client/ayon_core/hosts/fusion/deploy/ayon/fusion_shared.prefs b/server_addon/fusion/client/ayon_fusion/deploy/ayon/fusion_shared.prefs
similarity index 100%
rename from client/ayon_core/hosts/fusion/deploy/ayon/fusion_shared.prefs
rename to server_addon/fusion/client/ayon_fusion/deploy/ayon/fusion_shared.prefs
diff --git a/client/ayon_core/hosts/fusion/hooks/pre_fusion_launch_menu_hook.py b/server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_launch_menu_hook.py
similarity index 93%
rename from client/ayon_core/hosts/fusion/hooks/pre_fusion_launch_menu_hook.py
rename to server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_launch_menu_hook.py
index 113a1ffe59..035cbb8d97 100644
--- a/client/ayon_core/hosts/fusion/hooks/pre_fusion_launch_menu_hook.py
+++ b/server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_launch_menu_hook.py
@@ -1,6 +1,6 @@
import os
from ayon_applications import PreLaunchHook
-from ayon_core.hosts.fusion import FUSION_HOST_DIR
+from ayon_fusion import FUSION_ADDON_ROOT
class FusionLaunchMenuHook(PreLaunchHook):
@@ -28,7 +28,7 @@ class FusionLaunchMenuHook(PreLaunchHook):
"Validation for Fusion version 18+ for /execute "
"prelaunch argument skipped.")
- path = os.path.join(FUSION_HOST_DIR,
+ path = os.path.join(FUSION_ADDON_ROOT,
"deploy",
"MenuScripts",
"launch_menu.py").replace("\\", "/")
diff --git a/client/ayon_core/hosts/fusion/hooks/pre_fusion_profile_hook.py b/server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_profile_hook.py
similarity index 98%
rename from client/ayon_core/hosts/fusion/hooks/pre_fusion_profile_hook.py
rename to server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_profile_hook.py
index 1064d0a83a..7758798bb6 100644
--- a/client/ayon_core/hosts/fusion/hooks/pre_fusion_profile_hook.py
+++ b/server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_profile_hook.py
@@ -2,8 +2,8 @@ import os
import shutil
import platform
from pathlib import Path
-from ayon_core.hosts.fusion import (
- FUSION_HOST_DIR,
+from ayon_fusion import (
+ FUSION_ADDON_ROOT,
FUSION_VERSIONS_DICT,
get_fusion_version,
)
@@ -163,7 +163,7 @@ class FusionCopyPrefsPrelaunch(PreLaunchHook):
master_prefs_variable = f"FUSION{profile_version}_MasterPrefs"
master_prefs = Path(
- FUSION_HOST_DIR, "deploy", "ayon", "fusion_shared.prefs")
+ FUSION_ADDON_ROOT, "deploy", "ayon", "fusion_shared.prefs")
self.log.info(f"Setting {master_prefs_variable}: {master_prefs}")
self.launch_context.env[master_prefs_variable] = str(master_prefs)
diff --git a/client/ayon_core/hosts/fusion/hooks/pre_fusion_setup.py b/server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_setup.py
similarity index 92%
rename from client/ayon_core/hosts/fusion/hooks/pre_fusion_setup.py
rename to server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_setup.py
index ef084b0483..25cf40f18d 100644
--- a/client/ayon_core/hosts/fusion/hooks/pre_fusion_setup.py
+++ b/server_addon/fusion/client/ayon_fusion/hooks/pre_fusion_setup.py
@@ -4,8 +4,8 @@ from ayon_applications import (
LaunchTypes,
ApplicationLaunchFailed,
)
-from ayon_core.hosts.fusion import (
- FUSION_HOST_DIR,
+from ayon_fusion import (
+ FUSION_ADDON_ROOT,
FUSION_VERSIONS_DICT,
get_fusion_version,
)
@@ -67,5 +67,5 @@ class FusionPrelaunch(PreLaunchHook):
# for hook installing PySide2
self.data["fusion_python3_home"] = py3_dir
- self.log.info(f"Setting AYON_FUSION_ROOT: {FUSION_HOST_DIR}")
- self.launch_context.env["AYON_FUSION_ROOT"] = FUSION_HOST_DIR
+ self.log.info(f"Setting AYON_FUSION_ROOT: {FUSION_ADDON_ROOT}")
+ self.launch_context.env["AYON_FUSION_ROOT"] = FUSION_ADDON_ROOT
diff --git a/client/ayon_core/hosts/fusion/hooks/pre_pyside_install.py b/server_addon/fusion/client/ayon_fusion/hooks/pre_pyside_install.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/hooks/pre_pyside_install.py
rename to server_addon/fusion/client/ayon_fusion/hooks/pre_pyside_install.py
diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py b/server_addon/fusion/client/ayon_fusion/plugins/create/create_image_saver.py
similarity index 96%
rename from client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py
rename to server_addon/fusion/client/ayon_fusion/plugins/create/create_image_saver.py
index 729843d078..d88219b268 100644
--- a/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/create/create_image_saver.py
@@ -1,6 +1,6 @@
from ayon_core.lib import NumberDef
-from ayon_core.hosts.fusion.api.plugin import GenericCreateSaver
+from ayon_fusion.api.plugin import GenericCreateSaver
class CreateImageSaver(GenericCreateSaver):
diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py b/server_addon/fusion/client/ayon_fusion/plugins/create/create_saver.py
similarity index 97%
rename from client/ayon_core/hosts/fusion/plugins/create/create_saver.py
rename to server_addon/fusion/client/ayon_fusion/plugins/create/create_saver.py
index 20c7b99851..3e7d9486ce 100644
--- a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/create/create_saver.py
@@ -4,8 +4,8 @@ from ayon_core.lib import (
EnumDef
)
-from ayon_core.hosts.fusion.api.plugin import GenericCreateSaver
-from ayon_core.hosts.fusion.api.lib import get_current_comp
+from ayon_fusion.api.plugin import GenericCreateSaver
+from ayon_fusion.api.lib import get_current_comp
class CreateSaver(GenericCreateSaver):
diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py b/server_addon/fusion/client/ayon_fusion/plugins/create/create_workfile.py
similarity index 99%
rename from client/ayon_core/hosts/fusion/plugins/create/create_workfile.py
rename to server_addon/fusion/client/ayon_fusion/plugins/create/create_workfile.py
index a2fe027ef4..3dc14861df 100644
--- a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/create/create_workfile.py
@@ -1,6 +1,6 @@
import ayon_api
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
get_current_comp
)
from ayon_core.pipeline import (
diff --git a/client/ayon_core/hosts/fusion/plugins/inventory/select_containers.py b/server_addon/fusion/client/ayon_fusion/plugins/inventory/select_containers.py
similarity index 92%
rename from client/ayon_core/hosts/fusion/plugins/inventory/select_containers.py
rename to server_addon/fusion/client/ayon_fusion/plugins/inventory/select_containers.py
index 167cd3be1f..e863c58ab3 100644
--- a/client/ayon_core/hosts/fusion/plugins/inventory/select_containers.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/inventory/select_containers.py
@@ -8,7 +8,7 @@ class FusionSelectContainers(InventoryAction):
color = "#d8d8d8"
def process(self, containers):
- from ayon_core.hosts.fusion.api import (
+ from ayon_fusion.api import (
get_current_comp,
comp_lock_and_undo_chunk
)
diff --git a/client/ayon_core/hosts/fusion/plugins/inventory/set_tool_color.py b/server_addon/fusion/client/ayon_fusion/plugins/inventory/set_tool_color.py
similarity index 97%
rename from client/ayon_core/hosts/fusion/plugins/inventory/set_tool_color.py
rename to server_addon/fusion/client/ayon_fusion/plugins/inventory/set_tool_color.py
index 7167cf0fc5..2c02afe32c 100644
--- a/client/ayon_core/hosts/fusion/plugins/inventory/set_tool_color.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/inventory/set_tool_color.py
@@ -2,7 +2,7 @@ from qtpy import QtGui, QtWidgets
from ayon_core.pipeline import InventoryAction
from ayon_core import style
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
get_current_comp,
comp_lock_and_undo_chunk
)
diff --git a/client/ayon_core/hosts/fusion/plugins/load/actions.py b/server_addon/fusion/client/ayon_fusion/plugins/load/actions.py
similarity index 95%
rename from client/ayon_core/hosts/fusion/plugins/load/actions.py
rename to server_addon/fusion/client/ayon_fusion/plugins/load/actions.py
index 95400ea41c..dfa73e0b7a 100644
--- a/client/ayon_core/hosts/fusion/plugins/load/actions.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/load/actions.py
@@ -27,7 +27,7 @@ class FusionSetFrameRangeLoader(load.LoaderPlugin):
def load(self, context, name, namespace, data):
- from ayon_core.hosts.fusion.api import lib
+ from ayon_fusion.api import lib
version_attributes = context["version"]["attrib"]
@@ -63,7 +63,7 @@ class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin):
def load(self, context, name, namespace, data):
- from ayon_core.hosts.fusion.api import lib
+ from ayon_fusion.api import lib
version_attributes = context["version"]["attrib"]
start = version_attributes.get("frameStart", None)
diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py b/server_addon/fusion/client/ayon_fusion/plugins/load/load_alembic.py
similarity index 98%
rename from client/ayon_core/hosts/fusion/plugins/load/load_alembic.py
rename to server_addon/fusion/client/ayon_fusion/plugins/load/load_alembic.py
index 312362caca..2e763b5330 100644
--- a/client/ayon_core/hosts/fusion/plugins/load/load_alembic.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/load/load_alembic.py
@@ -2,7 +2,7 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
imprint_container,
get_current_comp,
comp_lock_and_undo_chunk
diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py b/server_addon/fusion/client/ayon_fusion/plugins/load/load_fbx.py
similarity index 98%
rename from client/ayon_core/hosts/fusion/plugins/load/load_fbx.py
rename to server_addon/fusion/client/ayon_fusion/plugins/load/load_fbx.py
index a84e7e0914..a080fa3983 100644
--- a/client/ayon_core/hosts/fusion/plugins/load/load_fbx.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/load/load_fbx.py
@@ -2,7 +2,7 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
imprint_container,
get_current_comp,
comp_lock_and_undo_chunk,
diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py b/server_addon/fusion/client/ayon_fusion/plugins/load/load_sequence.py
similarity index 99%
rename from client/ayon_core/hosts/fusion/plugins/load/load_sequence.py
rename to server_addon/fusion/client/ayon_fusion/plugins/load/load_sequence.py
index 7c70b54e48..233f1d7021 100644
--- a/client/ayon_core/hosts/fusion/plugins/load/load_sequence.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/load/load_sequence.py
@@ -1,7 +1,7 @@
import contextlib
import ayon_core.pipeline.load as load
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
imprint_container,
get_current_comp,
comp_lock_and_undo_chunk,
diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py b/server_addon/fusion/client/ayon_fusion/plugins/load/load_usd.py
similarity index 96%
rename from client/ayon_core/hosts/fusion/plugins/load/load_usd.py
rename to server_addon/fusion/client/ayon_fusion/plugins/load/load_usd.py
index 309b0c094c..42ce339faf 100644
--- a/client/ayon_core/hosts/fusion/plugins/load/load_usd.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/load/load_usd.py
@@ -2,12 +2,12 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
imprint_container,
get_current_comp,
comp_lock_and_undo_chunk
)
-from ayon_core.hosts.fusion.api.lib import get_fusion_module
+from ayon_fusion.api.lib import get_fusion_module
class FusionLoadUSD(load.LoaderPlugin):
diff --git a/client/ayon_core/hosts/fusion/plugins/load/load_workfile.py b/server_addon/fusion/client/ayon_fusion/plugins/load/load_workfile.py
similarity index 95%
rename from client/ayon_core/hosts/fusion/plugins/load/load_workfile.py
rename to server_addon/fusion/client/ayon_fusion/plugins/load/load_workfile.py
index 818fbcb187..c728f6b4aa 100644
--- a/client/ayon_core/hosts/fusion/plugins/load/load_workfile.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/load/load_workfile.py
@@ -5,7 +5,7 @@ is no update or reload function added for this plugin
from ayon_core.pipeline import load
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
get_current_comp,
get_bmd_library,
)
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_comp.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_comp.py
similarity index 91%
rename from client/ayon_core/hosts/fusion/plugins/publish/collect_comp.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/collect_comp.py
index 591c460d5a..2e5bcd63db 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/collect_comp.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_comp.py
@@ -1,6 +1,6 @@
import pyblish.api
-from ayon_core.hosts.fusion.api import get_current_comp
+from ayon_fusion.api import get_current_comp
class CollectCurrentCompFusion(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_comp_frame_range.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_comp_frame_range.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/collect_comp_frame_range.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/collect_comp_frame_range.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_inputs.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_inputs.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/collect_inputs.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/collect_inputs.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_instances.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/collect_instances.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_render.py
similarity index 97%
rename from client/ayon_core/hosts/fusion/plugins/publish/collect_render.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/collect_render.py
index 7a2844d5db..af52aee861 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_render.py
@@ -4,7 +4,7 @@ import pyblish.api
from ayon_core.pipeline import publish
from ayon_core.pipeline.publish import RenderInstance
-from ayon_core.hosts.fusion.api.lib import get_frame_path
+from ayon_fusion.api.lib import get_frame_path
@attr.s
@@ -52,7 +52,7 @@ class CollectFusionRender(
if product_type not in ["render", "image"]:
continue
- task_name = context.data["task"]
+ task_name = inst.data["task"]
tool = inst.data["transientData"]["tool"]
instance_families = inst.data.get("families", [])
@@ -115,6 +115,7 @@ class CollectFusionRender(
if "review" in instance.families:
# to skip ExtractReview locally
instance.families.remove("review")
+ instance.deadline = inst.data.get("deadline")
instances.append(instance)
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_workfile.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/collect_workfile.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/collect_workfile.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/collect_workfile.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/extract_render_local.py
similarity index 97%
rename from client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/extract_render_local.py
index 39fa20cfc0..bbcba5366d 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/extract_render_local.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/extract_render_local.py
@@ -5,8 +5,8 @@ import collections
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.fusion.api import comp_lock_and_undo_chunk
-from ayon_core.hosts.fusion.api.lib import get_frame_path, maintained_comp_range
+from ayon_fusion.api import comp_lock_and_undo_chunk
+from ayon_fusion.api.lib import get_frame_path, maintained_comp_range
log = logging.getLogger(__name__)
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/increment_current_file.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/increment_current_file.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/increment_current_file.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/increment_current_file.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/save_scene.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/save_scene.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/save_scene.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/save_scene.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_background_depth.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_background_depth.py
similarity index 95%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_background_depth.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_background_depth.py
index d588748cfa..90b6b110a4 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_background_depth.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_background_depth.py
@@ -6,7 +6,7 @@ from ayon_core.pipeline import (
PublishValidationError,
)
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
+from ayon_fusion.api.action import SelectInvalidAction
class ValidateBackgroundDepth(
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_comp_saved.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_comp_saved.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_comp_saved.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_comp_saved.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_create_folder_checked.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_create_folder_checked.py
similarity index 95%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_create_folder_checked.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_create_folder_checked.py
index 13ea85b48c..1b910123f0 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_create_folder_checked.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_create_folder_checked.py
@@ -3,7 +3,7 @@ import pyblish.api
from ayon_core.pipeline.publish import RepairAction
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
+from ayon_fusion.api.action import SelectInvalidAction
class ValidateCreateFolderChecked(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_expected_frames_existence.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_expected_frames_existence.py
similarity index 96%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_expected_frames_existence.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_expected_frames_existence.py
index 83d1feaefd..6dc9642581 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_expected_frames_existence.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_expected_frames_existence.py
@@ -4,7 +4,7 @@ import pyblish.api
from ayon_core.pipeline.publish import RepairAction
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
+from ayon_fusion.api.action import SelectInvalidAction
class ValidateLocalFramesExistence(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_filename_has_extension.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_filename_has_extension.py
similarity index 94%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_filename_has_extension.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_filename_has_extension.py
index 17b1aa47c8..471c0ca31a 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_filename_has_extension.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_filename_has_extension.py
@@ -3,7 +3,7 @@ import os
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
+from ayon_fusion.api.action import SelectInvalidAction
class ValidateFilenameHasExtension(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_image_frame.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_image_frame.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_image_frame.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_image_frame.py
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_instance_frame_range.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_instance_frame_range.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_instance_frame_range.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_instance_frame_range.py
diff --git a/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_instance_in_context.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_instance_in_context.py
new file mode 100644
index 0000000000..7b8b70b2fb
--- /dev/null
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_instance_in_context.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+"""Validate if instance context is the same as publish context."""
+
+import pyblish.api
+from ayon_fusion.api.action import SelectToolAction
+from ayon_core.pipeline.publish import (
+ RepairAction,
+ ValidateContentsOrder,
+ PublishValidationError,
+ OptionalPyblishPluginMixin
+)
+
+
+class ValidateInstanceInContextFusion(pyblish.api.InstancePlugin,
+ OptionalPyblishPluginMixin):
+ """Validator to check if instance context matches context of publish.
+
+ When working in per-shot style you always publish data in context of
+ current asset (shot). This validator checks if this is so. It is optional
+ so it can be disabled when needed.
+ """
+ # Similar to maya and houdini-equivalent `ValidateInstanceInContext`
+
+ order = ValidateContentsOrder
+ label = "Instance in same Context"
+ optional = True
+ hosts = ["fusion"]
+ actions = [SelectToolAction, RepairAction]
+
+ def process(self, instance):
+ if not self.is_active(instance.data):
+ return
+
+ instance_context = self.get_context(instance.data)
+ context = self.get_context(instance.context.data)
+ if instance_context != context:
+ context_label = "{} > {}".format(*context)
+ instance_label = "{} > {}".format(*instance_context)
+
+ raise PublishValidationError(
+ message=(
+ "Instance '{}' publishes to different asset than current "
+ "context: {}. Current context: {}".format(
+ instance.name, instance_label, context_label
+ )
+ ),
+ description=(
+ "## Publishing to a different asset\n"
+ "There are publish instances present which are publishing "
+ "into a different asset than your current context.\n\n"
+ "Usually this is not what you want but there can be cases "
+ "where you might want to publish into another asset or "
+ "shot. If that's the case you can disable the validation "
+ "on the instance to ignore it."
+ )
+ )
+
+ @classmethod
+ def repair(cls, instance):
+
+ create_context = instance.context.data["create_context"]
+ instance_id = instance.data.get("instance_id")
+ created_instance = create_context.get_instance_by_id(
+ instance_id
+ )
+ if created_instance is None:
+ raise RuntimeError(
+ f"No CreatedInstances found with id '{instance_id} "
+ f"in {create_context.instances_by_id}"
+ )
+
+ context_asset, context_task = cls.get_context(instance.context.data)
+ created_instance["folderPath"] = context_asset
+ created_instance["task"] = context_task
+ create_context.save_changes()
+
+ @staticmethod
+ def get_context(data):
+ """Return asset, task from publishing context data"""
+ return data["folderPath"], data["task"]
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_has_input.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_has_input.py
similarity index 93%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_saver_has_input.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_has_input.py
index a8977e4747..de2cd1d862 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_has_input.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_has_input.py
@@ -1,7 +1,7 @@
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
+from ayon_fusion.api.action import SelectInvalidAction
class ValidateSaverHasInput(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_passthrough.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_passthrough.py
similarity index 96%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_saver_passthrough.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_passthrough.py
index acafe3308f..caa17168bc 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_passthrough.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_passthrough.py
@@ -1,7 +1,7 @@
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
+from ayon_fusion.api.action import SelectInvalidAction
class ValidateSaverPassthrough(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_resolution.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_resolution.py
similarity index 96%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_saver_resolution.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_resolution.py
index 17992b123c..15d96a9afc 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_saver_resolution.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_saver_resolution.py
@@ -4,8 +4,8 @@ from ayon_core.pipeline import (
OptionalPyblishPluginMixin,
)
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
-from ayon_core.hosts.fusion.api import comp_lock_and_undo_chunk
+from ayon_fusion.api.action import SelectInvalidAction
+from ayon_fusion.api import comp_lock_and_undo_chunk
class ValidateSaverResolution(
diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_unique_subsets.py
similarity index 97%
rename from client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py
rename to server_addon/fusion/client/ayon_fusion/plugins/publish/validate_unique_subsets.py
index bcd9abd8b0..dd7df54da5 100644
--- a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py
+++ b/server_addon/fusion/client/ayon_fusion/plugins/publish/validate_unique_subsets.py
@@ -3,7 +3,7 @@ from collections import defaultdict
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.fusion.api.action import SelectInvalidAction
+from ayon_fusion.api.action import SelectInvalidAction
class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/__init__.py b/server_addon/fusion/client/ayon_fusion/scripts/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/flame/startup/openpype_babypublisher/modules/__init__.py
rename to server_addon/fusion/client/ayon_fusion/scripts/__init__.py
diff --git a/client/ayon_core/hosts/fusion/scripts/duplicate_with_inputs.py b/server_addon/fusion/client/ayon_fusion/scripts/duplicate_with_inputs.py
similarity index 97%
rename from client/ayon_core/hosts/fusion/scripts/duplicate_with_inputs.py
rename to server_addon/fusion/client/ayon_fusion/scripts/duplicate_with_inputs.py
index 727fd335ea..78edb1b3ba 100644
--- a/client/ayon_core/hosts/fusion/scripts/duplicate_with_inputs.py
+++ b/server_addon/fusion/client/ayon_fusion/scripts/duplicate_with_inputs.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.fusion.api import (
+from ayon_fusion.api import (
comp_lock_and_undo_chunk,
get_current_comp
)
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/__init__.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/__init__.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/__init__.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/__init__.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_cmp.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/_cmp.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_cmp.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_cmp.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_cmp.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/_cmp.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_cmp.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_cmp.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_compat.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/_compat.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_compat.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_compat.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_config.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/_config.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_config.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_config.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_funcs.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/_funcs.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_funcs.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_funcs.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_make.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/_make.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_make.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_make.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_next_gen.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/_next_gen.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_next_gen.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_next_gen.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_version_info.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/_version_info.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_version_info.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_version_info.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/_version_info.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/_version_info.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/_version_info.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/_version_info.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/converters.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/converters.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/converters.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/converters.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/converters.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/converters.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/converters.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/converters.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/exceptions.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/exceptions.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/exceptions.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/exceptions.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/exceptions.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/exceptions.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/exceptions.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/exceptions.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/filters.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/filters.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/filters.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/filters.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/filters.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/filters.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/filters.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/filters.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/py.typed b/server_addon/fusion/client/ayon_fusion/vendor/attr/py.typed
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/py.typed
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/py.typed
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/setters.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/setters.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/setters.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/setters.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/setters.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/setters.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/setters.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/setters.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/validators.py b/server_addon/fusion/client/ayon_fusion/vendor/attr/validators.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/validators.py
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/validators.py
diff --git a/client/ayon_core/hosts/fusion/vendor/attr/validators.pyi b/server_addon/fusion/client/ayon_fusion/vendor/attr/validators.pyi
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/attr/validators.pyi
rename to server_addon/fusion/client/ayon_fusion/vendor/attr/validators.pyi
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/_collections.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/_collections.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/_collections.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/_collections.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/_version.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/_version.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/_version.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/_version.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/connection.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/connection.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/connection.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/connection.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/connectionpool.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/connectionpool.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/connectionpool.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/connectionpool.py
diff --git a/client/ayon_core/hosts/fusion/scripts/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/scripts/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_appengine_environ.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_appengine_environ.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_appengine_environ.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_appengine_environ.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_securetransport/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_securetransport/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_securetransport/bindings.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_securetransport/bindings.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_securetransport/bindings.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_securetransport/bindings.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_securetransport/low_level.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_securetransport/low_level.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_securetransport/low_level.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/_securetransport/low_level.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/appengine.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/appengine.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/appengine.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/appengine.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/ntlmpool.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/ntlmpool.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/ntlmpool.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/ntlmpool.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/pyopenssl.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/pyopenssl.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/pyopenssl.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/pyopenssl.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/securetransport.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/securetransport.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/securetransport.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/securetransport.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/socks.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/socks.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/socks.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/contrib/socks.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/exceptions.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/exceptions.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/exceptions.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/exceptions.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/fields.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/fields.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/fields.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/fields.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/filepost.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/filepost.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/filepost.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/filepost.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/packages/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/packages/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_securetransport/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/backports/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/contrib/_securetransport/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/backports/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/packages/backports/makefile.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/backports/makefile.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/packages/backports/makefile.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/backports/makefile.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/packages/six.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/six.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/packages/six.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/six.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/packages/ssl_match_hostname/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/ssl_match_hostname/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/packages/ssl_match_hostname/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/ssl_match_hostname/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/packages/ssl_match_hostname/_implementation.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/ssl_match_hostname/_implementation.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/packages/ssl_match_hostname/_implementation.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/packages/ssl_match_hostname/_implementation.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/poolmanager.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/poolmanager.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/poolmanager.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/poolmanager.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/request.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/request.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/request.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/request.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/response.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/response.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/response.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/response.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/__init__.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/__init__.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/__init__.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/connection.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/connection.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/connection.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/connection.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/proxy.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/proxy.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/proxy.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/proxy.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/queue.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/queue.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/queue.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/queue.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/request.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/request.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/request.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/request.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/response.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/response.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/response.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/response.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/retry.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/retry.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/retry.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/retry.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/ssl_.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/ssl_.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/ssl_.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/ssl_.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/ssltransport.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/ssltransport.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/ssltransport.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/ssltransport.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/timeout.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/timeout.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/timeout.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/timeout.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/url.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/url.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/url.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/url.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/util/wait.py b/server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/wait.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/util/wait.py
rename to server_addon/fusion/client/ayon_fusion/vendor/urllib3/util/wait.py
diff --git a/server_addon/fusion/client/ayon_fusion/version.py b/server_addon/fusion/client/ayon_fusion/version.py
new file mode 100644
index 0000000000..209eddcdb6
--- /dev/null
+++ b/server_addon/fusion/client/ayon_fusion/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring AYON addon 'fusion' version."""
+__version__ = "0.2.0"
diff --git a/server_addon/fusion/package.py b/server_addon/fusion/package.py
index 9e7a46df2c..e82e9bf0f6 100644
--- a/server_addon/fusion/package.py
+++ b/server_addon/fusion/package.py
@@ -1,3 +1,10 @@
name = "fusion"
title = "Fusion"
-version = "0.1.5"
+version = "0.2.0"
+
+client_dir = "ayon_fusion"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/server_addon/harmony/package.py b/server_addon/harmony/package.py
index 83e88e7d57..00824cedef 100644
--- a/server_addon/harmony/package.py
+++ b/server_addon/harmony/package.py
@@ -1,3 +1,3 @@
name = "harmony"
title = "Harmony"
-version = "0.1.2"
+version = "0.1.3"
diff --git a/server_addon/harmony/server/settings/main.py b/server_addon/harmony/server/settings/main.py
index 9c780b63c2..8a72c966d8 100644
--- a/server_addon/harmony/server/settings/main.py
+++ b/server_addon/harmony/server/settings/main.py
@@ -45,11 +45,6 @@ DEFAULT_HARMONY_SETTING = {
"optional": True,
"active": True
},
- "ValidateContainers": {
- "enabled": True,
- "optional": True,
- "active": True
- },
"ValidateSceneSettings": {
"enabled": True,
"optional": True,
diff --git a/server_addon/harmony/server/settings/publish_plugins.py b/server_addon/harmony/server/settings/publish_plugins.py
index c9e7c515e4..2d976389f6 100644
--- a/server_addon/harmony/server/settings/publish_plugins.py
+++ b/server_addon/harmony/server/settings/publish_plugins.py
@@ -18,14 +18,6 @@ class ValidateAudioPlugin(BaseSettingsModel):
active: bool = SettingsField(True, title="Active")
-class ValidateContainersPlugin(BaseSettingsModel):
- """Check if loaded container is scene are latest versions."""
- _isGroup = True
- enabled: bool = True
- optional: bool = SettingsField(False, title="Optional")
- active: bool = SettingsField(True, title="Active")
-
-
class ValidateSceneSettingsPlugin(BaseSettingsModel):
"""Validate if FrameStart, FrameEnd and Resolution match shot data in DB.
Use regular expressions to limit validations only on particular asset
@@ -63,11 +55,6 @@ class HarmonyPublishPlugins(BaseSettingsModel):
default_factory=ValidateAudioPlugin,
)
- ValidateContainers: ValidateContainersPlugin = SettingsField(
- title="Validate Containers",
- default_factory=ValidateContainersPlugin,
- )
-
ValidateSceneSettings: ValidateSceneSettingsPlugin = SettingsField(
title="Validate Scene Settings",
default_factory=ValidateSceneSettingsPlugin,
diff --git a/client/ayon_core/hosts/houdini/__init__.py b/server_addon/houdini/client/ayon_houdini/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/houdini/__init__.py
rename to server_addon/houdini/client/ayon_houdini/__init__.py
diff --git a/client/ayon_core/hosts/houdini/addon.py b/server_addon/houdini/client/ayon_houdini/addon.py
similarity index 100%
rename from client/ayon_core/hosts/houdini/addon.py
rename to server_addon/houdini/client/ayon_houdini/addon.py
diff --git a/client/ayon_core/hosts/houdini/api/__init__.py b/server_addon/houdini/client/ayon_houdini/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/houdini/api/__init__.py
rename to server_addon/houdini/client/ayon_houdini/api/__init__.py
diff --git a/client/ayon_core/hosts/houdini/api/action.py b/server_addon/houdini/client/ayon_houdini/api/action.py
similarity index 100%
rename from client/ayon_core/hosts/houdini/api/action.py
rename to server_addon/houdini/client/ayon_houdini/api/action.py
diff --git a/client/ayon_core/hosts/houdini/api/colorspace.py b/server_addon/houdini/client/ayon_houdini/api/colorspace.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/api/colorspace.py
rename to server_addon/houdini/client/ayon_houdini/api/colorspace.py
index 66581d6f20..ec6e4c2091 100644
--- a/client/ayon_core/hosts/houdini/api/colorspace.py
+++ b/server_addon/houdini/client/ayon_houdini/api/colorspace.py
@@ -1,6 +1,6 @@
import attr
import hou
-from ayon_core.hosts.houdini.api.lib import get_color_management_preferences
+from ayon_houdini.api.lib import get_color_management_preferences
from ayon_core.pipeline.colorspace import get_display_view_colorspace_name
@attr.s
diff --git a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py b/server_addon/houdini/client/ayon_houdini/api/creator_node_shelves.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/api/creator_node_shelves.py
rename to server_addon/houdini/client/ayon_houdini/api/creator_node_shelves.py
index 72c157f187..4d5a706749 100644
--- a/client/ayon_core/hosts/houdini/api/creator_node_shelves.py
+++ b/server_addon/houdini/client/ayon_houdini/api/creator_node_shelves.py
@@ -35,7 +35,7 @@ CATEGORY_GENERIC_TOOL = {
CREATE_SCRIPT = """
-from ayon_core.hosts.houdini.api.creator_node_shelves import create_interactive
+from ayon_houdini.api.creator_node_shelves import create_interactive
create_interactive("{identifier}", **kwargs)
"""
@@ -153,7 +153,7 @@ def install():
This function is re-entrant and can be called again to reinstall and
update the node definitions. For example during development it can be
useful to call it manually:
- >>> from ayon_core.hosts.houdini.api.creator_node_shelves import install
+ >>> from ayon_houdini.api.creator_node_shelves import install
>>> install()
Returns:
diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/server_addon/houdini/client/ayon_houdini/api/lib.py
similarity index 99%
rename from client/ayon_core/hosts/houdini/api/lib.py
rename to server_addon/houdini/client/ayon_houdini/api/lib.py
index 7ca8f7f8f0..671265fae9 100644
--- a/client/ayon_core/hosts/houdini/api/lib.py
+++ b/server_addon/houdini/client/ayon_houdini/api/lib.py
@@ -1027,7 +1027,7 @@ def add_self_publish_button(node):
button_parm = hou.ButtonParmTemplate(
"ayon_self_publish",
"{} Publish".format(label),
- script_callback="from ayon_core.hosts.houdini.api.lib import "
+ script_callback="from ayon_houdini.api.lib import "
"self_publish; self_publish()",
script_callback_language=hou.scriptLanguage.Python,
join_with_next=True
@@ -1070,7 +1070,7 @@ def sceneview_snapshot(
Example:
This is how the function can be used::
- from ayon_core.hosts.houdini.api import lib
+ from ayon_houdini.api import lib
sceneview = hou.ui.paneTabOfType(hou.paneTabType.SceneViewer)
lib.sceneview_snapshot(sceneview)
diff --git a/client/ayon_core/hosts/houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/api/pipeline.py
rename to server_addon/houdini/client/ayon_houdini/api/pipeline.py
index 4797cf36a0..6af4993d25 100644
--- a/client/ayon_core/hosts/houdini/api/pipeline.py
+++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py
@@ -17,8 +17,8 @@ from ayon_core.pipeline import (
AYON_CONTAINER_ID,
)
from ayon_core.pipeline.load import any_outdated_containers
-from ayon_core.hosts.houdini import HOUDINI_HOST_DIR
-from ayon_core.hosts.houdini.api import lib, shelves, creator_node_shelves
+from ayon_houdini import HOUDINI_HOST_DIR
+from ayon_houdini.api import lib, shelves, creator_node_shelves
from ayon_core.lib import (
register_event_callback,
@@ -26,7 +26,7 @@ from ayon_core.lib import (
)
-log = logging.getLogger("ayon_core.hosts.houdini")
+log = logging.getLogger("ayon_houdini")
AVALON_CONTAINERS = "/obj/AVALON_CONTAINERS"
CONTEXT_CONTAINER = "/obj/OpenPypeContext"
diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/server_addon/houdini/client/ayon_houdini/api/plugin.py
similarity index 90%
rename from client/ayon_core/hosts/houdini/api/plugin.py
rename to server_addon/houdini/client/ayon_houdini/api/plugin.py
index a9c8c313b9..9c6bba925a 100644
--- a/client/ayon_core/hosts/houdini/api/plugin.py
+++ b/server_addon/houdini/client/ayon_houdini/api/plugin.py
@@ -7,6 +7,7 @@ from abc import (
import six
import hou
+import pyblish.api
from ayon_core.pipeline import (
CreatorError,
LegacyCreator,
@@ -14,11 +15,17 @@ from ayon_core.pipeline import (
CreatedInstance,
AYON_INSTANCE_ID,
AVALON_INSTANCE_ID,
+ load,
+ publish
)
from ayon_core.lib import BoolDef
+
from .lib import imprint, read, lsattr, add_self_publish_button
+SETTINGS_CATEGORY = "houdini"
+
+
class Creator(LegacyCreator):
"""Creator plugin to create instances in Houdini
@@ -169,6 +176,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
settings_name = None
add_publish_button = False
+ settings_category = SETTINGS_CATEGORY
+
def create(self, product_name, instance_data, pre_create_data):
try:
self.selected_nodes = []
@@ -347,3 +356,39 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
for key, value in settings.items():
setattr(self, key, value)
+
+
+class HoudiniLoader(load.LoaderPlugin):
+ """Base class for Houdini load plugins."""
+
+ hosts = ["houdini"]
+ settings_category = SETTINGS_CATEGORY
+
+
+class HoudiniInstancePlugin(pyblish.api.InstancePlugin):
+ """Base class for Houdini instance publish plugins."""
+
+ hosts = ["houdini"]
+ settings_category = SETTINGS_CATEGORY
+
+
+class HoudiniContextPlugin(pyblish.api.ContextPlugin):
+ """Base class for Houdini context publish plugins."""
+
+ hosts = ["houdini"]
+ settings_category = SETTINGS_CATEGORY
+
+
+class HoudiniExtractorPlugin(publish.Extractor):
+ """Base class for Houdini extract plugins.
+
+ Note:
+ The `HoudiniExtractorPlugin` is a subclass of `publish.Extractor`,
+ which in turn is a subclass of `pyblish.api.InstancePlugin`.
+ Should there be a requirement to create an extractor that operates
+ as a context plugin, it would be beneficial to incorporate
+ the functionalities present in `publish.Extractor`.
+ """
+
+ hosts = ["houdini"]
+ settings_category = SETTINGS_CATEGORY
diff --git a/client/ayon_core/hosts/houdini/api/shelves.py b/server_addon/houdini/client/ayon_houdini/api/shelves.py
similarity index 99%
rename from client/ayon_core/hosts/houdini/api/shelves.py
rename to server_addon/houdini/client/ayon_houdini/api/shelves.py
index b178139020..2987568af1 100644
--- a/client/ayon_core/hosts/houdini/api/shelves.py
+++ b/server_addon/houdini/client/ayon_houdini/api/shelves.py
@@ -12,7 +12,7 @@ import hou
from .lib import get_current_context_template_data_with_folder_attrs
-log = logging.getLogger("ayon_core.hosts.houdini.shelves")
+log = logging.getLogger("ayon_houdini.shelves")
def generate_shelves():
diff --git a/client/ayon_core/hosts/houdini/api/usd.py b/server_addon/houdini/client/ayon_houdini/api/usd.py
similarity index 100%
rename from client/ayon_core/hosts/houdini/api/usd.py
rename to server_addon/houdini/client/ayon_houdini/api/usd.py
diff --git a/client/ayon_core/hosts/houdini/hooks/set_default_display_and_view.py b/server_addon/houdini/client/ayon_houdini/hooks/set_default_display_and_view.py
similarity index 100%
rename from client/ayon_core/hosts/houdini/hooks/set_default_display_and_view.py
rename to server_addon/houdini/client/ayon_houdini/hooks/set_default_display_and_view.py
diff --git a/client/ayon_core/hosts/houdini/hooks/set_paths.py b/server_addon/houdini/client/ayon_houdini/hooks/set_paths.py
similarity index 100%
rename from client/ayon_core/hosts/houdini/hooks/set_paths.py
rename to server_addon/houdini/client/ayon_houdini/hooks/set_paths.py
diff --git a/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py b/server_addon/houdini/client/ayon_houdini/plugins/create/convert_legacy.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/convert_legacy.py
index 1a4761172a..4c8c8062ce 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/convert_legacy.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/convert_legacy.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Converter for legacy Houdini products."""
from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin
-from ayon_core.hosts.houdini.api.lib import imprint
+from ayon_houdini.api.lib import imprint
class HoudiniLegacyConvertor(ProductConvertorPlugin):
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_alembic_camera.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_alembic_camera.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/create/create_alembic_camera.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_alembic_camera.py
index 0ab5e2794e..4a92e24671 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_alembic_camera.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_alembic_camera.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating alembic camera products."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.pipeline import CreatorError
import hou
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_ass.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_arnold_ass.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/create/create_arnold_ass.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_arnold_ass.py
index be5604c01c..4f5fb5833e 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_ass.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_arnold_ass.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating Arnold ASS files."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import BoolDef
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_arnold_rop.py
similarity index 61%
rename from client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_arnold_rop.py
index f65b54a452..43875ccbd6 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_arnold_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_arnold_rop.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import EnumDef, BoolDef
@@ -13,11 +13,17 @@ class CreateArnoldRop(plugin.HoudiniCreator):
# Default extension
ext = "exr"
- # Default to split export and render jobs
- export_job = True
+ # Default render target
+ render_target = "farm_split"
def create(self, product_name, instance_data, pre_create_data):
import hou
+ # Transfer settings from pre create to instance
+ creator_attributes = instance_data.setdefault(
+ "creator_attributes", dict())
+ for key in ["render_target", "review"]:
+ if key in pre_create_data:
+ creator_attributes[key] = pre_create_data[key]
# Remove the active, we are checking the bypass flag of the nodes
instance_data.pop("active", None)
@@ -25,8 +31,6 @@ class CreateArnoldRop(plugin.HoudiniCreator):
# Add chunk size attribute
instance_data["chunkSize"] = 1
- # Submit for job publishing
- instance_data["farm"] = pre_create_data.get("farm")
instance = super(CreateArnoldRop, self).create(
product_name,
@@ -51,7 +55,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
"ar_exr_half_precision": 1 # half precision
}
- if pre_create_data.get("export_job"):
+ if pre_create_data.get("render_target") == "farm_split":
ass_filepath = \
"{export_dir}{product_name}/{product_name}.$F4.ass".format(
export_dir=hou.text.expandString("$HIP/pyblish/ass/"),
@@ -66,23 +70,41 @@ class CreateArnoldRop(plugin.HoudiniCreator):
to_lock = ["productType", "id"]
self.lock_parameters(instance_node, to_lock)
- def get_pre_create_attr_defs(self):
- attrs = super(CreateArnoldRop, self).get_pre_create_attr_defs()
+ def get_instance_attr_defs(self):
+ """get instance attribute definitions.
+ Attributes defined in this method are exposed in
+ publish tab in the publisher UI.
+ """
+
+ render_target_items = {
+ "local": "Local machine rendering",
+ "local_no_render": "Use existing frames (local)",
+ "farm": "Farm Rendering",
+ "farm_split": "Farm Rendering - Split export & render jobs",
+ }
+
+ return [
+ BoolDef("review",
+ label="Review",
+ tooltip="Mark as reviewable",
+ default=True),
+ EnumDef("render_target",
+ items=render_target_items,
+ label="Render target",
+ default=self.render_target),
+ ]
+
+ def get_pre_create_attr_defs(self):
image_format_enum = [
"bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png",
"rad", "rat", "rta", "sgi", "tga", "tif",
]
- return attrs + [
- BoolDef("farm",
- label="Submitting to Farm",
- default=True),
- BoolDef("export_job",
- label="Split export and render jobs",
- default=self.export_job),
+ attrs = [
EnumDef("image_format",
image_format_enum,
default=self.ext,
- label="Image Format Options")
+ label="Image Format Options"),
]
+ return attrs + self.get_instance_attr_defs()
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_bgeo.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_bgeo.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/create/create_bgeo.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_bgeo.py
index 3749598b1d..93cf0e0998 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_bgeo.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_bgeo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating pointcache bgeo files."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.pipeline import CreatorError
import hou
from ayon_core.lib import EnumDef, BoolDef
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_composite.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_composite.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/create/create_composite.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_composite.py
index a25faf0e8e..8c0ee8a099 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_composite.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_composite.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating composite sequences."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.pipeline import CreatorError
import hou
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/create/create_hda.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py
index d399aa5e15..6a1adce8cc 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py
@@ -3,7 +3,7 @@
import ayon_api
from ayon_core.pipeline import CreatorError
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
import hou
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_karma_rop.py
similarity index 74%
rename from client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_karma_rop.py
index e91ddbc0ac..693e6295e2 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_karma_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_karma_rop.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin to create Karma ROP."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import BoolDef, EnumDef, NumberDef
@@ -11,15 +11,23 @@ class CreateKarmaROP(plugin.HoudiniCreator):
product_type = "karma_rop"
icon = "magic"
+ # Default render target
+ render_target = "farm"
+
def create(self, product_name, instance_data, pre_create_data):
import hou # noqa
+ # Transfer settings from pre create to instance
+ creator_attributes = instance_data.setdefault(
+ "creator_attributes", dict())
+
+ for key in ["render_target", "review"]:
+ if key in pre_create_data:
+ creator_attributes[key] = pre_create_data[key]
instance_data.pop("active", None)
instance_data.update({"node_type": "karma"})
# Add chunk size attribute
instance_data["chunkSize"] = 10
- # Submit for job publishing
- instance_data["farm"] = pre_create_data.get("farm")
instance = super(CreateKarmaROP, self).create(
product_name,
@@ -86,18 +94,40 @@ class CreateKarmaROP(plugin.HoudiniCreator):
to_lock = ["productType", "id"]
self.lock_parameters(instance_node, to_lock)
- def get_pre_create_attr_defs(self):
- attrs = super(CreateKarmaROP, self).get_pre_create_attr_defs()
+ def get_instance_attr_defs(self):
+ """get instance attribute definitions.
+ Attributes defined in this method are exposed in
+ publish tab in the publisher UI.
+ """
+
+ render_target_items = {
+ "local": "Local machine rendering",
+ "local_no_render": "Use existing frames (local)",
+ "farm": "Farm Rendering",
+ }
+
+ return [
+ BoolDef("review",
+ label="Review",
+ tooltip="Mark as reviewable",
+ default=True),
+ EnumDef("render_target",
+ items=render_target_items,
+ label="Render target",
+ default=self.render_target)
+ ]
+
+
+ def get_pre_create_attr_defs(self):
image_format_enum = [
"bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png",
"rad", "rat", "rta", "sgi", "tga", "tif",
]
- return attrs + [
- BoolDef("farm",
- label="Submitting to Farm",
- default=True),
+ attrs = super(CreateKarmaROP, self).get_pre_create_attr_defs()
+
+ attrs += [
EnumDef("image_format",
image_format_enum,
default="exr",
@@ -112,5 +142,6 @@ class CreateKarmaROP(plugin.HoudiniCreator):
decimals=0),
BoolDef("cam_res",
label="Camera Resolution",
- default=False)
+ default=False),
]
+ return attrs + self.get_instance_attr_defs()
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_ifd.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_ifd.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/create/create_mantra_ifd.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_ifd.py
index e0cf035c35..fc5c4819d0 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_ifd.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_ifd.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating pointcache alembics."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import BoolDef
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_rop.py
similarity index 68%
rename from client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_rop.py
index 64ecf428e9..ce1c96f8b2 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_mantra_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_rop.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin to create Mantra ROP."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import EnumDef, BoolDef
@@ -11,18 +11,22 @@ class CreateMantraROP(plugin.HoudiniCreator):
product_type = "mantra_rop"
icon = "magic"
- # Default to split export and render jobs
- export_job = True
+ # Default render target
+ render_target = "farm_split"
def create(self, product_name, instance_data, pre_create_data):
import hou # noqa
+ # Transfer settings from pre create to instance
+ creator_attributes = instance_data.setdefault(
+ "creator_attributes", dict())
+ for key in ["render_target", "review"]:
+ if key in pre_create_data:
+ creator_attributes[key] = pre_create_data[key]
instance_data.pop("active", None)
instance_data.update({"node_type": "ifd"})
# Add chunk size attribute
instance_data["chunkSize"] = 10
- # Submit for job publishing
- instance_data["farm"] = pre_create_data.get("farm")
instance = super(CreateMantraROP, self).create(
product_name,
@@ -46,7 +50,7 @@ class CreateMantraROP(plugin.HoudiniCreator):
"vm_picture": filepath,
}
- if pre_create_data.get("export_job"):
+ if pre_create_data.get("render_target") == "farm_split":
ifd_filepath = \
"{export_dir}{product_name}/{product_name}.$F4.ifd".format(
export_dir=hou.text.expandString("$HIP/pyblish/ifd/"),
@@ -77,21 +81,40 @@ class CreateMantraROP(plugin.HoudiniCreator):
to_lock = ["productType", "id"]
self.lock_parameters(instance_node, to_lock)
- def get_pre_create_attr_defs(self):
- attrs = super(CreateMantraROP, self).get_pre_create_attr_defs()
+ def get_instance_attr_defs(self):
+ """get instance attribute definitions.
+ Attributes defined in this method are exposed in
+ publish tab in the publisher UI.
+ """
+
+ render_target_items = {
+ "local": "Local machine rendering",
+ "local_no_render": "Use existing frames (local)",
+ "farm": "Farm Rendering",
+ "farm_split": "Farm Rendering - Split export & render jobs",
+ }
+
+ return [
+ BoolDef("review",
+ label="Review",
+ tooltip="Mark as reviewable",
+ default=True),
+ EnumDef("render_target",
+ items=render_target_items,
+ label="Render target",
+ default=self.render_target)
+ ]
+
+ def get_pre_create_attr_defs(self):
image_format_enum = [
"bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png",
"rad", "rat", "rta", "sgi", "tga", "tif",
]
- return attrs + [
- BoolDef("farm",
- label="Submitting to Farm",
- default=True),
- BoolDef("export_job",
- label="Split export and render jobs",
- default=self.export_job),
+ attrs = super(CreateMantraROP, self).get_pre_create_attr_defs()
+
+ attrs += [
EnumDef("image_format",
image_format_enum,
default="exr",
@@ -100,5 +123,6 @@ class CreateMantraROP(plugin.HoudiniCreator):
label="Override Camera Resolution",
tooltip="Override the current camera "
"resolution, recommended for IPR.",
- default=False)
+ default=False),
]
+ return attrs + self.get_instance_attr_defs()
diff --git a/server_addon/houdini/client/ayon_houdini/plugins/create/create_model.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_model.py
new file mode 100644
index 0000000000..ed6b2096c5
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_model.py
@@ -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_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()
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_pointcache.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_pointcache.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/create/create_pointcache.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_pointcache.py
index 9e0a335c3a..6a63659053 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_pointcache.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_pointcache.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating pointcache alembics."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import BoolDef
import hou
@@ -105,7 +105,7 @@ class CreatePointCache(plugin.HoudiniCreator):
elif len(outputs) == 1:
return outputs[0]
- # if there are more than one, then it have multiple ouput nodes
+ # if there are more than one, then it have multiple output nodes
# return the one with the minimum 'outputidx'
else:
return min(outputs,
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_proxy.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_redshift_proxy.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/create/create_redshift_proxy.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_redshift_proxy.py
index 6a9321b95a..0e3eb03ddd 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_proxy.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_redshift_proxy.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating Redshift proxies."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
import hou
from ayon_core.lib import BoolDef
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_redshift_rop.py
similarity index 76%
rename from client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_redshift_rop.py
index 1cd239e929..d63e584692 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_redshift_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_redshift_rop.py
@@ -3,7 +3,7 @@
import hou # noqa
from ayon_core.pipeline import CreatorError
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import EnumDef, BoolDef
@@ -17,17 +17,21 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
ext = "exr"
multi_layered_mode = "No Multi-Layered EXR File"
- # Default to split export and render jobs
- split_render = True
+ # Default render target
+ render_target = "farm_split"
def create(self, product_name, instance_data, pre_create_data):
+ # Transfer settings from pre create to instance
+ creator_attributes = instance_data.setdefault(
+ "creator_attributes", dict())
+ for key in ["render_target", "review"]:
+ if key in pre_create_data:
+ creator_attributes[key] = pre_create_data[key]
instance_data.pop("active", None)
instance_data.update({"node_type": "Redshift_ROP"})
# Add chunk size attribute
instance_data["chunkSize"] = 10
- # Submit for job publishing
- instance_data["farm"] = pre_create_data.get("farm")
instance = super(CreateRedshiftROP, self).create(
product_name,
@@ -99,7 +103,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
rs_filepath = f"{export_dir}{product_name}/{product_name}.$F4.rs"
parms["RS_archive_file"] = rs_filepath
- if pre_create_data.get("split_render", self.split_render):
+ if pre_create_data.get("render_target") == "farm_split":
parms["RS_archive_enable"] = 1
instance_node.setParms(parms)
@@ -118,24 +122,44 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
return super(CreateRedshiftROP, self).remove_instances(instances)
+ def get_instance_attr_defs(self):
+ """get instance attribute definitions.
+
+ Attributes defined in this method are exposed in
+ publish tab in the publisher UI.
+ """
+
+ render_target_items = {
+ "local": "Local machine rendering",
+ "local_no_render": "Use existing frames (local)",
+ "farm": "Farm Rendering",
+ "farm_split": "Farm Rendering - Split export & render jobs",
+ }
+
+ return [
+ BoolDef("review",
+ label="Review",
+ tooltip="Mark as reviewable",
+ default=True),
+ EnumDef("render_target",
+ items=render_target_items,
+ label="Render target",
+ default=self.render_target)
+ ]
+
def get_pre_create_attr_defs(self):
- attrs = super(CreateRedshiftROP, self).get_pre_create_attr_defs()
+
image_format_enum = [
"exr", "tif", "jpg", "png",
]
+
multi_layered_mode = [
"No Multi-Layered EXR File",
"Full Multi-Layered EXR File"
]
-
- return attrs + [
- BoolDef("farm",
- label="Submitting to Farm",
- default=True),
- BoolDef("split_render",
- label="Split export and render jobs",
- default=self.split_render),
+ attrs = super(CreateRedshiftROP, self).get_pre_create_attr_defs()
+ attrs += [
EnumDef("image_format",
image_format_enum,
default=self.ext,
@@ -143,5 +167,6 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
EnumDef("multi_layered_mode",
multi_layered_mode,
default=self.multi_layered_mode,
- label="Multi-Layered EXR")
+ label="Multi-Layered EXR"),
]
+ return attrs + self.get_instance_attr_defs()
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_review.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_review.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/create/create_review.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_review.py
index f5e4d4ce64..b27264f400 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_review.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_review.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating openGL reviews."""
-from ayon_core.hosts.houdini.api import lib, plugin
+from ayon_houdini.api import lib, plugin
from ayon_core.lib import EnumDef, BoolDef, NumberDef
import os
@@ -103,7 +103,7 @@ class CreateReview(plugin.HoudiniCreator):
# cls.review_color_space is an empty string
# when the imageio/workfile setting is disabled or
# when the Review colorspace setting is empty.
- from ayon_core.hosts.houdini.api.colorspace import get_default_display_view_colorspace # noqa
+ from ayon_houdini.api.colorspace import get_default_display_view_colorspace # noqa
self.review_color_space = get_default_display_view_colorspace()
lib.set_review_color_space(instance_node,
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_staticmesh.py
similarity index 99%
rename from client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_staticmesh.py
index 3271107c6e..17b646040c 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_staticmesh.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_staticmesh.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator for Unreal Static Meshes."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import BoolDef, EnumDef
import hou
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_usd.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_usd.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/create/create_usd.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_usd.py
index 700f7eefd6..b8aede677b 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_usd.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_usd.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating USDs."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
import hou
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_usdrender.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_usdrender.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/create/create_usdrender.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_usdrender.py
index 36197e349e..a34de1fec4 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_usdrender.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_usdrender.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating USD renders."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
class CreateUSDRender(plugin.HoudiniCreator):
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vbd_cache.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_vbd_cache.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/create/create_vbd_cache.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_vbd_cache.py
index c34cd2b4b5..e8c0920ec8 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_vbd_cache.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_vbd_cache.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating VDB Caches."""
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.lib import BoolDef
import hou
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_vray_rop.py
similarity index 82%
rename from client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_vray_rop.py
index 5ed9e848a7..d15ee23825 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_vray_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_vray_rop.py
@@ -2,7 +2,7 @@
"""Creator plugin to create VRay ROP."""
import hou
-from ayon_core.hosts.houdini.api import plugin
+from ayon_houdini.api import plugin
from ayon_core.pipeline import CreatorError
from ayon_core.lib import EnumDef, BoolDef
@@ -16,17 +16,21 @@ class CreateVrayROP(plugin.HoudiniCreator):
icon = "magic"
ext = "exr"
- # Default to split export and render jobs
- export_job = True
+ # Default render target
+ render_target = "farm_split"
def create(self, product_name, instance_data, pre_create_data):
+ # Transfer settings from pre create to instance
+ creator_attributes = instance_data.setdefault(
+ "creator_attributes", dict())
+ for key in ["render_target", "review"]:
+ if key in pre_create_data:
+ creator_attributes[key] = pre_create_data[key]
instance_data.pop("active", None)
instance_data.update({"node_type": "vray_renderer"})
# Add chunk size attribute
instance_data["chunkSize"] = 10
- # Submit for job publishing
- instance_data["farm"] = pre_create_data.get("farm")
instance = super(CreateVrayROP, self).create(
product_name,
@@ -55,7 +59,7 @@ class CreateVrayROP(plugin.HoudiniCreator):
"SettingsEXR_bits_per_channel": "16" # half precision
}
- if pre_create_data.get("export_job"):
+ if pre_create_data.get("render_target") == "farm_split":
scene_filepath = \
"{export_dir}{product_name}/{product_name}.$F4.vrscene".format(
export_dir=hou.text.expandString("$HIP/pyblish/vrscene/"),
@@ -143,20 +147,41 @@ class CreateVrayROP(plugin.HoudiniCreator):
return super(CreateVrayROP, self).remove_instances(instances)
+ def get_instance_attr_defs(self):
+ """get instance attribute definitions.
+
+ Attributes defined in this method are exposed in
+ publish tab in the publisher UI.
+ """
+
+
+ render_target_items = {
+ "local": "Local machine rendering",
+ "local_no_render": "Use existing frames (local)",
+ "farm": "Farm Rendering",
+ "farm_split": "Farm Rendering - Split export & render jobs",
+ }
+
+ return [
+ BoolDef("review",
+ label="Review",
+ tooltip="Mark as reviewable",
+ default=True),
+ EnumDef("render_target",
+ items=render_target_items,
+ label="Render target",
+ default=self.render_target)
+ ]
+
def get_pre_create_attr_defs(self):
- attrs = super(CreateVrayROP, self).get_pre_create_attr_defs()
image_format_enum = [
"bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png",
"rad", "rat", "rta", "sgi", "tga", "tif",
]
- return attrs + [
- BoolDef("farm",
- label="Submitting to Farm",
- default=True),
- BoolDef("export_job",
- label="Split export and render jobs",
- default=self.export_job),
+ attrs = super(CreateVrayROP, self).get_pre_create_attr_defs()
+
+ attrs += [
EnumDef("image_format",
image_format_enum,
default=self.ext,
@@ -172,3 +197,4 @@ class CreateVrayROP(plugin.HoudiniCreator):
"if enabled",
default=False)
]
+ return attrs + self.get_instance_attr_defs()
diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_workfile.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/create/create_workfile.py
rename to server_addon/houdini/client/ayon_houdini/plugins/create/create_workfile.py
index a958509e25..babf602855 100644
--- a/client/ayon_core/hosts/houdini/plugins/create/create_workfile.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_workfile.py
@@ -2,9 +2,9 @@
"""Creator plugin for creating workfiles."""
import ayon_api
-from ayon_core.hosts.houdini.api import plugin
-from ayon_core.hosts.houdini.api.lib import read, imprint
-from ayon_core.hosts.houdini.api.pipeline import CONTEXT_CONTAINER
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import read, imprint
+from ayon_houdini.api.pipeline import CONTEXT_CONTAINER
from ayon_core.pipeline import CreatedInstance, AutoCreator
import hou
@@ -95,7 +95,7 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator):
# write workfile information to context container.
op_ctx = hou.node(CONTEXT_CONTAINER)
if not op_ctx:
- op_ctx = self.create_context_node()
+ op_ctx = self.host.create_context_node()
workfile_data = {"workfile": current_instance.data_to_store()}
imprint(op_ctx, workfile_data)
diff --git a/client/ayon_core/hosts/houdini/plugins/inventory/set_camera_resolution.py b/server_addon/houdini/client/ayon_houdini/plugins/inventory/set_camera_resolution.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/inventory/set_camera_resolution.py
rename to server_addon/houdini/client/ayon_houdini/plugins/inventory/set_camera_resolution.py
index 4cebd537bb..e2f8fcfa9b 100644
--- a/client/ayon_core/hosts/houdini/plugins/inventory/set_camera_resolution.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/inventory/set_camera_resolution.py
@@ -1,5 +1,5 @@
from ayon_core.pipeline import InventoryAction
-from ayon_core.hosts.houdini.api.lib import (
+from ayon_houdini.api.lib import (
get_camera_from_container,
set_camera_resolution
)
diff --git a/client/ayon_core/hosts/houdini/plugins/load/actions.py b/server_addon/houdini/client/ayon_houdini/plugins/load/actions.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/load/actions.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/actions.py
index 3e9cc35504..5fe545ced9 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/actions.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/actions.py
@@ -2,10 +2,10 @@
"""
-from ayon_core.pipeline import load
+from ayon_houdini.api import plugin
-class SetFrameRangeLoader(load.LoaderPlugin):
+class SetFrameRangeLoader(plugin.HoudiniLoader):
"""Set frame range excluding pre- and post-handles"""
product_types = {
@@ -42,7 +42,7 @@ class SetFrameRangeLoader(load.LoaderPlugin):
hou.playbar.setPlaybackRange(start, end)
-class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
+class SetFrameRangeWithHandlesLoader(plugin.HoudiniLoader):
"""Set frame range including pre- and post-handles"""
product_types = {
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_alembic.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/load/load_alembic.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_alembic.py
index 5f04781501..7db2fe93ed 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_alembic.py
@@ -1,12 +1,12 @@
import os
-from ayon_core.pipeline import (
- load,
- get_representation_path,
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import (
+ pipeline,
+ plugin
)
-from ayon_core.hosts.houdini.api import pipeline
-class AbcLoader(load.LoaderPlugin):
+class AbcLoader(plugin.HoudiniLoader):
"""Load Alembic"""
product_types = {"model", "animation", "pointcache", "gpuCache"}
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_alembic_archive.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_alembic_archive.py
index a231bd9993..a34a43e48a 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_alembic_archive.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_alembic_archive.py
@@ -1,12 +1,13 @@
+
import os
-from ayon_core.pipeline import (
- load,
- get_representation_path,
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import (
+ pipeline,
+ plugin
)
-from ayon_core.hosts.houdini.api import pipeline
-class AbcArchiveLoader(load.LoaderPlugin):
+class AbcArchiveLoader(plugin.HoudiniLoader):
"""Load Alembic as full geometry network hierarchy """
product_types = {"model", "animation", "pointcache", "gpuCache"}
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_ass.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/load/load_ass.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_ass.py
index 6e0922e305..5fd97bc2a6 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_ass.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_ass.py
@@ -1,14 +1,14 @@
import os
import re
-from ayon_core.pipeline import (
- load,
- get_representation_path,
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import (
+ pipeline,
+ plugin
)
-from ayon_core.hosts.houdini.api import pipeline
-class AssLoader(load.LoaderPlugin):
+class AssLoader(plugin.HoudiniLoader):
"""Load .ass with Arnold Procedural"""
product_types = {"ass"}
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_bgeo.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_bgeo.py
index a318b71963..7119612cda 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_bgeo.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_bgeo.py
@@ -2,14 +2,14 @@
import os
import re
-from ayon_core.pipeline import (
- load,
- get_representation_path,
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import (
+ pipeline,
+ plugin
)
-from ayon_core.hosts.houdini.api import pipeline
-class BgeoLoader(load.LoaderPlugin):
+class BgeoLoader(plugin.HoudiniLoader):
"""Load bgeo files to Houdini."""
label = "Load bgeo"
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_camera.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/load/load_camera.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_camera.py
index b7912f88f1..b597519813 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_camera.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_camera.py
@@ -1,16 +1,15 @@
-from ayon_core.pipeline import (
- load,
- get_representation_path,
-)
-from ayon_core.hosts.houdini.api import pipeline
+import hou
+from ayon_core.pipeline import get_representation_path
-from ayon_core.hosts.houdini.api.lib import (
+from ayon_houdini.api import (
+ pipeline,
+ plugin
+)
+from ayon_houdini.api.lib import (
set_camera_resolution,
get_camera_from_container
)
-import hou
-
ARCHIVE_EXPRESSION = ('__import__("_alembic_hom_extensions")'
'.alembicGetCameraDict')
@@ -84,7 +83,7 @@ def transfer_non_default_values(src, dest, ignore=None):
dest_parm.setFromParm(parm)
-class CameraLoader(load.LoaderPlugin):
+class CameraLoader(plugin.HoudiniLoader):
"""Load camera from an Alembic file"""
product_types = {"camera"}
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_fbx.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/load/load_fbx.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_fbx.py
index 398019a3bd..273ca43bc4 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_fbx.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_fbx.py
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
"""Fbx Loader for houdini. """
-from ayon_core.pipeline import (
- load,
- get_representation_path,
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import (
+ pipeline,
+ plugin
)
-from ayon_core.hosts.houdini.api import pipeline
-class FbxLoader(load.LoaderPlugin):
+class FbxLoader(plugin.HoudiniLoader):
"""Load fbx files. """
label = "Load FBX"
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_filepath.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_filepath.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/load/load_filepath.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_filepath.py
index d189a027fd..2ce9bd7ffb 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_filepath.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_filepath.py
@@ -1,13 +1,14 @@
import os
import re
-
-from ayon_core.pipeline import load
-from ayon_core.hosts.houdini.api import pipeline
-
import hou
+from ayon_houdini.api import (
+ pipeline,
+ plugin
+)
-class FilePathLoader(load.LoaderPlugin):
+
+class FilePathLoader(plugin.HoudiniLoader):
"""Load a managed filepath to a null node.
This is useful if for a particular workflow there is no existing loader
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/load/load_hda.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py
index 10fc03be03..b04e211aa4 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
import os
-from ayon_core.pipeline import (
- load,
- get_representation_path,
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import (
+ pipeline,
+ plugin
)
-from ayon_core.hosts.houdini.api import pipeline
-class HdaLoader(load.LoaderPlugin):
+class HdaLoader(plugin.HoudiniLoader):
"""Load Houdini Digital Asset file."""
product_types = {"hda"}
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_image.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_image.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/load/load_image.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_image.py
index dfbd3c11eb..9d4cd2fb18 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_image.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_image.py
@@ -1,14 +1,16 @@
import os
import re
+import hou
from ayon_core.pipeline import (
- load,
get_representation_path,
AVALON_CONTAINER_ID,
)
-from ayon_core.hosts.houdini.api import lib, pipeline
-
-import hou
+from ayon_houdini.api import (
+ pipeline,
+ plugin,
+ lib
+)
def get_image_avalon_container():
@@ -42,7 +44,7 @@ def get_image_avalon_container():
return image_container
-class ImageLoader(load.LoaderPlugin):
+class ImageLoader(plugin.HoudiniLoader):
"""Load images into COP2"""
product_types = {
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_redshift_proxy.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_redshift_proxy.py
index f09856a970..514dbe109f 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_redshift_proxy.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_redshift_proxy.py
@@ -1,16 +1,17 @@
import os
import re
-from ayon_core.pipeline import (
- load,
- get_representation_path,
-)
-from ayon_core.hosts.houdini.api import pipeline
-from ayon_core.pipeline.load import LoadError
-
import hou
+from ayon_core.pipeline import get_representation_path
+from ayon_core.pipeline.load import LoadError
-class RedshiftProxyLoader(load.LoaderPlugin):
+from ayon_houdini.api import (
+ pipeline,
+ plugin
+)
+
+
+class RedshiftProxyLoader(plugin.HoudiniLoader):
"""Load Redshift Proxy"""
product_types = {"redshiftproxy"}
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_layer.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_layer.py
index 4e6954c531..fb302fd943 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_layer.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_layer.py
@@ -1,12 +1,14 @@
from ayon_core.pipeline import (
- load,
get_representation_path,
AVALON_CONTAINER_ID,
)
-from ayon_core.hosts.houdini.api import lib
+from ayon_houdini.api import (
+ plugin,
+ lib
+)
-class USDSublayerLoader(load.LoaderPlugin):
+class USDSublayerLoader(plugin.HoudiniLoader):
"""Sublayer USD file in Solaris"""
product_types = {
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_reference.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_reference.py
index 7e82a6abd0..690f6ce187 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_reference.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_reference.py
@@ -1,12 +1,14 @@
from ayon_core.pipeline import (
- load,
get_representation_path,
AVALON_CONTAINER_ID,
)
-from ayon_core.hosts.houdini.api import lib
+from ayon_houdini.api import (
+ plugin,
+ lib
+)
-class USDReferenceLoader(load.LoaderPlugin):
+class USDReferenceLoader(plugin.HoudiniLoader):
"""Reference USD file in Solaris"""
product_types = {
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_sop.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_sop.py
index 506f6140bf..347e3283de 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_usd_sop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_usd_sop.py
@@ -1,10 +1,12 @@
import os
-from ayon_core.pipeline import load
-from ayon_core.hosts.houdini.api import pipeline
+from ayon_houdini.api import (
+ pipeline,
+ plugin
+)
-class SopUsdImportLoader(load.LoaderPlugin):
+class SopUsdImportLoader(plugin.HoudiniLoader):
"""Load USD to SOPs via `usdimport`"""
label = "Load USD to SOPs"
diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_vdb.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/load/load_vdb.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/load_vdb.py
index 0008f0d5f8..9014f4c5e2 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/load_vdb.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_vdb.py
@@ -1,14 +1,14 @@
import os
import re
-from ayon_core.pipeline import (
- load,
- get_representation_path,
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import (
+ pipeline,
+ plugin
)
-from ayon_core.hosts.houdini.api import pipeline
-class VdbLoader(load.LoaderPlugin):
+class VdbLoader(plugin.HoudiniLoader):
"""Load VDB"""
product_types = {"vdbcache"}
diff --git a/client/ayon_core/hosts/houdini/plugins/load/show_usdview.py b/server_addon/houdini/client/ayon_houdini/plugins/load/show_usdview.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/load/show_usdview.py
rename to server_addon/houdini/client/ayon_houdini/plugins/load/show_usdview.py
index 0158a6b963..4e18bc038a 100644
--- a/client/ayon_core/hosts/houdini/plugins/load/show_usdview.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/load/show_usdview.py
@@ -3,10 +3,10 @@ import platform
import subprocess
from ayon_core.lib.vendor_bin_utils import find_executable
-from ayon_core.pipeline import load
+from ayon_houdini.api import plugin
-class ShowInUsdview(load.LoaderPlugin):
+class ShowInUsdview(plugin.HoudiniLoader):
"""Open USD file in usdview"""
label = "Show in usdview"
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_active_state.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_active_state.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_active_state.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_active_state.py
index 7fda94b288..e09a347e9f 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_active_state.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_active_state.py
@@ -1,8 +1,10 @@
-import pyblish.api
import hou
+import pyblish.api
+from ayon_houdini.api import plugin
-class CollectInstanceActiveState(pyblish.api.InstancePlugin):
+
+class CollectInstanceActiveState(plugin.HoudiniInstancePlugin):
"""Collect default active state for instance from its node bypass state.
This is done at the very end of the CollectorOrder so that any required
@@ -14,7 +16,6 @@ class CollectInstanceActiveState(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.299
families = ["*"]
- hosts = ["houdini"]
label = "Instance Active State"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_arnold_rop.py
similarity index 87%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_arnold_rop.py
index 7fe38555a3..10c6d91d26 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_arnold_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_arnold_rop.py
@@ -4,12 +4,14 @@ import re
import hou
import pyblish.api
-from ayon_core.hosts.houdini.api import colorspace
-from ayon_core.hosts.houdini.api.lib import (
- evalParmNoFrame, get_color_management_preferences)
+from ayon_houdini.api import colorspace, plugin
+from ayon_houdini.api.lib import (
+ get_color_management_preferences,
+ evalParmNoFrame
+)
-class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin):
+class CollectArnoldROPRenderProducts(plugin.HoudiniInstancePlugin):
"""Collect Arnold ROP Render Products
Collects the instance.data["files"] for the render products.
@@ -23,7 +25,6 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin):
# This specific order value is used so that
# this plugin runs after CollectFrames
order = pyblish.api.CollectorOrder + 0.11
- hosts = ["houdini"]
families = ["arnold_rop"]
def process(self, instance):
@@ -40,12 +41,9 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin):
default_prefix = evalParmNoFrame(rop, "ar_picture")
render_products = []
- # Store whether we are splitting the render job (export + render)
- split_render = bool(rop.parm("ar_ass_export_enable").eval())
- instance.data["splitRender"] = split_render
export_prefix = None
export_products = []
- if split_render:
+ if instance.data["splitRender"]:
export_prefix = evalParmNoFrame(
rop, "ar_ass_file", pad_character="0"
)
@@ -68,7 +66,12 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin):
"": self.generate_expected_files(instance, beauty_product)
}
+ # Assume it's a multipartExr Render.
+ multipartExr = True
+
num_aovs = rop.evalParm("ar_aovs")
+ # TODO: Check the following logic.
+ # as it always assumes that all AOV are not merged.
for index in range(1, num_aovs + 1):
# Skip disabled AOVs
if not rop.evalParm("ar_enable_aov{}".format(index)):
@@ -85,6 +88,14 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin):
files_by_aov[label] = self.generate_expected_files(instance,
aov_product)
+ # Set to False as soon as we have a separated aov.
+ multipartExr = False
+
+ # Review Logic expects this key to exist and be True
+ # if render is a multipart Exr.
+ # As long as we have one AOV then multipartExr should be True.
+ instance.data["multipartExr"] = multipartExr
+
for product in render_products:
self.log.debug("Found render product: {}".format(product))
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_asset_handles.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_asset_handles.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_asset_handles.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_asset_handles.py
index 943a29952e..db9bde8595 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_asset_handles.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_asset_handles.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
"""Collector plugin for frames data on ROP instances."""
-import hou # noqa
import pyblish.api
from ayon_core.lib import BoolDef
from ayon_core.pipeline import AYONPyblishPluginMixin
+from ayon_houdini.api import plugin
-class CollectAssetHandles(pyblish.api.InstancePlugin,
+class CollectAssetHandles(plugin.HoudiniInstancePlugin,
AYONPyblishPluginMixin):
"""Apply folder handles.
@@ -23,8 +23,6 @@ class CollectAssetHandles(pyblish.api.InstancePlugin,
the exclusive frame range and actual handle ranges.
"""
- hosts = ["houdini"]
-
# This specific order value is used so that
# this plugin runs after CollectAnatomyInstanceData
order = pyblish.api.CollectorOrder + 0.499
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_cache_farm.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_cache_farm.py
similarity index 85%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_cache_farm.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_cache_farm.py
index 040ad68a1a..ecfebccfef 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_cache_farm.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_cache_farm.py
@@ -1,17 +1,20 @@
import os
-import pyblish.api
import hou
-from ayon_core.hosts.houdini.api import lib
+import pyblish.api
+from ayon_houdini.api import (
+ lib,
+ plugin
+)
-class CollectDataforCache(pyblish.api.InstancePlugin):
+class CollectDataforCache(plugin.HoudiniInstancePlugin):
"""Collect data for caching to Deadline."""
- order = pyblish.api.CollectorOrder + 0.04
+ # Run after Collect Frames
+ order = pyblish.api.CollectorOrder + 0.11
families = ["ass", "pointcache",
"mantraifd", "redshiftproxy",
- "vdbcache"]
- hosts = ["houdini"]
+ "vdbcache", "model"]
targets = ["local", "remote"]
label = "Collect Data for Cache"
@@ -42,10 +45,7 @@ class CollectDataforCache(pyblish.api.InstancePlugin):
cache_files = {"_": instance.data["files"]}
# Convert instance family to pointcache if it is bgeo or abc
# because ???
- for family in instance.data["families"]:
- if family == "bgeo" or "abc":
- instance.data["productType"] = "pointcache"
- break
+ self.log.debug(instance.data["families"])
instance.data.update({
"plugin": "Houdini",
"publish": True
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_chunk_size.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_chunk_size.py
similarity index 88%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_chunk_size.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_chunk_size.py
index 3e2561dd6f..6ff53b7695 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_chunk_size.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_chunk_size.py
@@ -1,17 +1,17 @@
import pyblish.api
from ayon_core.lib import NumberDef
from ayon_core.pipeline import AYONPyblishPluginMixin
+from ayon_houdini.api import plugin
-class CollectChunkSize(pyblish.api.InstancePlugin,
+class CollectChunkSize(plugin.HoudiniInstancePlugin,
AYONPyblishPluginMixin):
"""Collect chunk size for cache submission to Deadline."""
order = pyblish.api.CollectorOrder + 0.05
families = ["ass", "pointcache",
"vdbcache", "mantraifd",
- "redshiftproxy"]
- hosts = ["houdini"]
+ "redshiftproxy", "model"]
targets = ["local", "remote"]
label = "Collect Chunk Size"
chunk_size = 999999
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_current_file.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_current_file.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_current_file.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_current_file.py
index 7b55778803..8e339e0e04 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_current_file.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_current_file.py
@@ -2,14 +2,14 @@ import os
import hou
import pyblish.api
+from ayon_houdini.api import plugin
-class CollectHoudiniCurrentFile(pyblish.api.ContextPlugin):
+class CollectHoudiniCurrentFile(plugin.HoudiniContextPlugin):
"""Inject the current working file into context"""
order = pyblish.api.CollectorOrder - 0.1
label = "Houdini Current File"
- hosts = ["houdini"]
def process(self, context):
"""Inject the current working file"""
diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_farm_instances.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_farm_instances.py
new file mode 100644
index 0000000000..8fdae06f90
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_farm_instances.py
@@ -0,0 +1,35 @@
+import pyblish.api
+from ayon_houdini.api import plugin
+
+
+class CollectFarmInstances(plugin.HoudiniInstancePlugin):
+ """Collect instances for farm render."""
+
+ order = pyblish.api.CollectorOrder
+ families = ["mantra_rop",
+ "karma_rop",
+ "redshift_rop",
+ "arnold_rop",
+ "vray_rop"]
+
+ targets = ["local", "remote"]
+ label = "Collect farm instances"
+
+ def process(self, instance):
+
+ creator_attribute = instance.data["creator_attributes"]
+
+ # Collect Render Target
+ if creator_attribute.get("render_target") not in {
+ "farm_split", "farm"
+ }:
+ instance.data["farm"] = False
+ instance.data["splitRender"] = False
+ self.log.debug("Render on farm is disabled. "
+ "Skipping farm collecting.")
+ return
+
+ instance.data["farm"] = True
+ instance.data["splitRender"] = (
+ creator_attribute.get("render_target") == "farm_split"
+ )
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_frames.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_frames.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py
index a643ab0d38..3378657bfd 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_frames.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py
@@ -5,10 +5,10 @@ import re
import hou # noqa
import pyblish.api
-from ayon_core.hosts.houdini.api import lib
+from ayon_houdini.api import lib, plugin
-class CollectFrames(pyblish.api.InstancePlugin):
+class CollectFrames(plugin.HoudiniInstancePlugin):
"""Collect all frames which would be saved from the ROP nodes"""
# This specific order value is used so that
@@ -17,7 +17,7 @@ class CollectFrames(pyblish.api.InstancePlugin):
label = "Collect Frames"
families = ["vdbcache", "imagesequence", "ass",
"mantraifd", "redshiftproxy", "review",
- "bgeo"]
+ "pointcache"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_inputs.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_inputs.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_inputs.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_inputs.py
index 6cf6bbf430..f2904a68f6 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_inputs.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_inputs.py
@@ -1,8 +1,8 @@
from collections import deque
import pyblish.api
-
from ayon_core.pipeline import registered_host
+from ayon_houdini.api import plugin
def get_container_members(container):
@@ -80,7 +80,7 @@ def iter_upstream(node):
collected.update(ancestors)
-class CollectUpstreamInputs(pyblish.api.InstancePlugin):
+class CollectUpstreamInputs(plugin.HoudiniInstancePlugin):
"""Collect source input containers used for this publish.
This will include `inputs` data of which loaded publishes were used in the
@@ -91,7 +91,6 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin):
label = "Collect Inputs"
order = pyblish.api.CollectorOrder + 0.4
- hosts = ["houdini"]
def process(self, instance):
# We can't get the "inputAncestors" directly from the ROP
diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_instances_type.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_instances_type.py
new file mode 100644
index 0000000000..542abf8139
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_instances_type.py
@@ -0,0 +1,23 @@
+"""Collector for different types.
+
+This will add additional families to different instance based on
+the creator_identifier parameter.
+"""
+import pyblish.api
+
+
+class CollectPointcacheType(pyblish.api.InstancePlugin):
+ """Collect data type for different instances."""
+
+ order = pyblish.api.CollectorOrder
+ families = ["pointcache", "model"]
+ label = "Collect instances types"
+
+ def process(self, instance):
+ if instance.data["creator_identifier"] == "io.openpype.creators.houdini.bgeo": # noqa: E501
+ instance.data["families"] += ["bgeo"]
+ elif instance.data["creator_identifier"] in {
+ "io.openpype.creators.houdini.pointcache",
+ "io.openpype.creators.houdini.model"
+ }:
+ instance.data["families"] += ["abc"]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_instances_usd_layered.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_instances_usd_layered.py
index 9377a9fcd0..4f85a629fb 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_instances_usd_layered.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_instances_usd_layered.py
@@ -1,11 +1,11 @@
import hou
import pyblish.api
-from ayon_core.hosts.houdini.api import lib
-import ayon_core.hosts.houdini.api.usd as hou_usdlib
from ayon_core.pipeline import usdlib
+from ayon_houdini.api import lib, plugin
+import ayon_houdini.api.usd as hou_usdlib
-class CollectInstancesUsdLayered(pyblish.api.ContextPlugin):
+class CollectInstancesUsdLayered(plugin.HoudiniContextPlugin):
"""Collect Instances from a ROP Network and its configured layer paths.
The output nodes of the ROP node will only be published when *any* of the
@@ -32,7 +32,6 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin):
order = pyblish.api.CollectorOrder - 0.01
label = "Collect Instances (USD Configured Layers)"
- hosts = ["houdini"]
def process(self, context):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_karma_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_karma_rop.py
similarity index 87%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_karma_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_karma_rop.py
index 78651b0c69..60fec9d2e0 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_karma_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_karma_rop.py
@@ -4,16 +4,17 @@ import os
import hou
import pyblish.api
-from ayon_core.hosts.houdini.api.lib import (
+from ayon_houdini.api.lib import (
evalParmNoFrame,
get_color_management_preferences
)
-from ayon_core.hosts.houdini.api import (
- colorspace
+from ayon_houdini.api import (
+ colorspace,
+ plugin
)
-class CollectKarmaROPRenderProducts(pyblish.api.InstancePlugin):
+class CollectKarmaROPRenderProducts(plugin.HoudiniInstancePlugin):
"""Collect Karma Render Products
Collects the instance.data["files"] for the multipart render product.
@@ -27,7 +28,6 @@ class CollectKarmaROPRenderProducts(pyblish.api.InstancePlugin):
# This specific order value is used so that
# this plugin runs after CollectFrames
order = pyblish.api.CollectorOrder + 0.11
- hosts = ["houdini"]
families = ["karma_rop"]
def process(self, instance):
@@ -55,6 +55,12 @@ class CollectKarmaROPRenderProducts(pyblish.api.InstancePlugin):
beauty_product)
}
+ # Review Logic expects this key to exist and be True
+ # if render is a multipart Exr.
+ # As long as we have one AOV then multipartExr should be True.
+ # By default karma render is a multipart Exr.
+ instance.data["multipartExr"] = True
+
filenames = list(render_products)
instance.data["files"] = filenames
instance.data["renderProducts"] = colorspace.ARenderProduct()
diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_local_render_instances.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_local_render_instances.py
new file mode 100644
index 0000000000..259b2378bb
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_local_render_instances.py
@@ -0,0 +1,137 @@
+import os
+import pyblish.api
+from ayon_core.pipeline.create import get_product_name
+from ayon_core.pipeline.farm.patterning import match_aov_pattern
+from ayon_core.pipeline.publish import (
+ get_plugin_settings,
+ apply_plugin_settings_automatically
+)
+from ayon_houdini.api import plugin
+
+
+class CollectLocalRenderInstances(plugin.HoudiniInstancePlugin):
+ """Collect instances for local render.
+
+ Agnostic Local Render Collector.
+ """
+
+ # this plugin runs after Collect Render Products
+ order = pyblish.api.CollectorOrder + 0.12
+ families = ["mantra_rop",
+ "karma_rop",
+ "redshift_rop",
+ "arnold_rop",
+ "vray_rop"]
+
+ label = "Collect local render instances"
+
+ use_deadline_aov_filter = False
+ aov_filter = {"host_name": "houdini",
+ "value": [".*([Bb]eauty).*"]}
+
+ @classmethod
+ def apply_settings(cls, project_settings):
+ # Preserve automatic settings applying logic
+ settings = get_plugin_settings(plugin=cls,
+ project_settings=project_settings,
+ log=cls.log,
+ category="houdini")
+ apply_plugin_settings_automatically(cls, settings, logger=cls.log)
+
+ if not cls.use_deadline_aov_filter:
+ # get aov_filter from collector settings
+ # and restructure it as match_aov_pattern requires.
+ cls.aov_filter = {
+ cls.aov_filter["host_name"]: cls.aov_filter["value"]
+ }
+ else:
+ # get aov_filter from deadline settings
+ cls.aov_filter = project_settings["deadline"]["publish"]["ProcessSubmittedJobOnFarm"]["aov_filter"]
+ cls.aov_filter = {
+ item["name"]: item["value"]
+ for item in cls.aov_filter
+ }
+
+ def process(self, instance):
+
+ if instance.data["farm"]:
+ self.log.debug("Render on farm is enabled. "
+ "Skipping local render collecting.")
+ return
+
+ # Create Instance for each AOV.
+ context = instance.context
+ expectedFiles = next(iter(instance.data["expectedFiles"]), {})
+
+ product_type = "render" # is always render
+ product_group = get_product_name(
+ context.data["projectName"],
+ context.data["taskEntity"]["name"],
+ context.data["taskEntity"]["taskType"],
+ context.data["hostName"],
+ product_type,
+ instance.data["productName"]
+ )
+
+ for aov_name, aov_filepaths in expectedFiles.items():
+ product_name = product_group
+
+ if aov_name:
+ product_name = "{}_{}".format(product_name, aov_name)
+
+ # Create instance for each AOV
+ aov_instance = context.create_instance(product_name)
+
+ # Prepare Representation for each AOV
+ aov_filenames = [os.path.basename(path) for path in aov_filepaths]
+ staging_dir = os.path.dirname(aov_filepaths[0])
+ ext = aov_filepaths[0].split(".")[-1]
+
+ # Decide if instance is reviewable
+ preview = False
+ if instance.data.get("multipartExr", False):
+ # Add preview tag because its multipartExr.
+ preview = True
+ else:
+ # Add Preview tag if the AOV matches the filter.
+ preview = match_aov_pattern(
+ "houdini", self.aov_filter, aov_filenames[0]
+ )
+
+ preview = preview and instance.data.get("review", False)
+
+ # Support Single frame.
+ # The integrator wants single files to be a single
+ # filename instead of a list.
+ # More info: https://github.com/ynput/ayon-core/issues/238
+ if len(aov_filenames) == 1:
+ aov_filenames = aov_filenames[0]
+
+ aov_instance.data.update({
+ # 'label': label,
+ "task": instance.data["task"],
+ "folderPath": instance.data["folderPath"],
+ "frameStart": instance.data["frameStartHandle"],
+ "frameEnd": instance.data["frameEndHandle"],
+ "productType": product_type,
+ "family": product_type,
+ "productName": product_name,
+ "productGroup": product_group,
+ "families": ["render.local.hou", "review"],
+ "instance_node": instance.data["instance_node"],
+ "representations": [
+ {
+ "stagingDir": staging_dir,
+ "ext": ext,
+ "name": ext,
+ "tags": ["review"] if preview else [],
+ "files": aov_filenames,
+ "frameStart": instance.data["frameStartHandle"],
+ "frameEnd": instance.data["frameEndHandle"]
+ }
+ ]
+ })
+
+ # Skip integrating original render instance.
+ # We are not removing it because it's used to trigger the render.
+ instance.data["integrate"] = False
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_mantra_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_mantra_rop.py
similarity index 86%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_mantra_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_mantra_rop.py
index df9acc4b61..f7feeee63b 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_mantra_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_mantra_rop.py
@@ -4,16 +4,17 @@ import os
import hou
import pyblish.api
-from ayon_core.hosts.houdini.api.lib import (
+from ayon_houdini.api.lib import (
evalParmNoFrame,
get_color_management_preferences
)
-from ayon_core.hosts.houdini.api import (
- colorspace
+from ayon_houdini.api import (
+ colorspace,
+ plugin
)
-class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin):
+class CollectMantraROPRenderProducts(plugin.HoudiniInstancePlugin):
"""Collect Mantra Render Products
Collects the instance.data["files"] for the render products.
@@ -27,7 +28,6 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin):
# This specific order value is used so that
# this plugin runs after CollectFrames
order = pyblish.api.CollectorOrder + 0.11
- hosts = ["houdini"]
families = ["mantra_rop"]
def process(self, instance):
@@ -44,12 +44,9 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin):
default_prefix = evalParmNoFrame(rop, "vm_picture")
render_products = []
- # Store whether we are splitting the render job (export + render)
- split_render = bool(rop.parm("soho_outputmode").eval())
- instance.data["splitRender"] = split_render
export_prefix = None
export_products = []
- if split_render:
+ if instance.data["splitRender"]:
export_prefix = evalParmNoFrame(
rop, "soho_diskfile", pad_character="0"
)
@@ -74,6 +71,11 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin):
beauty_product)
}
+ # Assume it's a multipartExr Render.
+ multipartExr = True
+
+ # TODO: This logic doesn't take into considerations
+ # cryptomatte defined in 'Images > Cryptomatte'
aov_numbers = rop.evalParm("vm_numaux")
if aov_numbers > 0:
# get the filenames of the AOVs
@@ -93,6 +95,14 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin):
files_by_aov[var] = self.generate_expected_files(instance, aov_product) # noqa
+ # Set to False as soon as we have a separated aov.
+ multipartExr = False
+
+ # Review Logic expects this key to exist and be True
+ # if render is a multipart Exr.
+ # As long as we have one AOV then multipartExr should be True.
+ instance.data["multipartExr"] = multipartExr
+
for product in render_products:
self.log.debug("Found render product: %s" % product)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_output_node.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_output_node.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_output_node.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_output_node.py
index 26381e065e..ff51669376 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_output_node.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_output_node.py
@@ -1,9 +1,9 @@
import pyblish.api
-
from ayon_core.pipeline.publish import KnownPublishError
+from ayon_houdini.api import plugin
-class CollectOutputSOPPath(pyblish.api.InstancePlugin):
+class CollectOutputSOPPath(plugin.HoudiniInstancePlugin):
"""Collect the out node's SOP/COP Path value."""
order = pyblish.api.CollectorOrder
@@ -15,10 +15,10 @@ class CollectOutputSOPPath(pyblish.api.InstancePlugin):
"usd",
"usdrender",
"redshiftproxy",
- "staticMesh"
+ "staticMesh",
+ "model"
]
- hosts = ["houdini"]
label = "Collect Output Node Path"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_redshift_rop.py
similarity index 90%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_redshift_rop.py
index 55a55bb12a..96cb6ebeaf 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_redshift_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_redshift_rop.py
@@ -4,16 +4,17 @@ import os
import hou
import pyblish.api
-from ayon_core.hosts.houdini.api.lib import (
+from ayon_houdini.api.lib import (
evalParmNoFrame,
get_color_management_preferences
)
-from ayon_core.hosts.houdini.api import (
- colorspace
+from ayon_houdini.api import (
+ colorspace,
+ plugin
)
-class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
+class CollectRedshiftROPRenderProducts(plugin.HoudiniInstancePlugin):
"""Collect USD Render Products
Collects the instance.data["files"] for the render products.
@@ -27,7 +28,6 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
# This specific order value is used so that
# this plugin runs after CollectFrames
order = pyblish.api.CollectorOrder + 0.11
- hosts = ["houdini"]
families = ["redshift_rop"]
def process(self, instance):
@@ -42,11 +42,9 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
default_prefix = evalParmNoFrame(rop, "RS_outputFileNamePrefix")
beauty_suffix = rop.evalParm("RS_outputBeautyAOVSuffix")
- # Store whether we are splitting the render job (export + render)
- split_render = bool(rop.parm("RS_archive_enable").eval())
- instance.data["splitRender"] = split_render
+
export_products = []
- if split_render:
+ if instance.data["splitRender"]:
export_prefix = evalParmNoFrame(
rop, "RS_archive_file", pad_character="0"
)
@@ -63,9 +61,12 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
full_exr_mode = (rop.evalParm("RS_outputMultilayerMode") == "2")
if full_exr_mode:
# Ignore beauty suffix if full mode is enabled
- # As this is what the rop does.
+ # As this is what the rop does.
beauty_suffix = ""
+ # Assume it's a multipartExr Render.
+ multipartExr = True
+
# Default beauty/main layer AOV
beauty_product = self.get_render_product_name(
prefix=default_prefix, suffix=beauty_suffix
@@ -75,7 +76,7 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
beauty_suffix: self.generate_expected_files(instance,
beauty_product)
}
-
+
aovs_rop = rop.parm("RS_aovGetFromNode").evalAsNode()
if aovs_rop:
rop = aovs_rop
@@ -98,13 +99,21 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
if rop.parm(f"RS_aovID_{i}").evalAsString() == "CRYPTOMATTE" or \
not full_exr_mode:
-
+
aov_product = self.get_render_product_name(aov_prefix, aov_suffix)
render_products.append(aov_product)
files_by_aov[aov_suffix] = self.generate_expected_files(instance,
aov_product) # noqa
+ # Set to False as soon as we have a separated aov.
+ multipartExr = False
+
+ # Review Logic expects this key to exist and be True
+ # if render is a multipart Exr.
+ # As long as we have one AOV then multipartExr should be True.
+ instance.data["multipartExr"] = multipartExr
+
for product in render_products:
self.log.debug("Found render product: %s" % product)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_remote_publish.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_remote_publish.py
similarity index 83%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_remote_publish.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_remote_publish.py
index 5d459f525e..e695b57518 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_remote_publish.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_remote_publish.py
@@ -1,16 +1,15 @@
+import hou
import pyblish.api
-import hou
from ayon_core.pipeline.publish import RepairAction
-from ayon_core.hosts.houdini.api import lib
+from ayon_houdini.api import lib, plugin
-class CollectRemotePublishSettings(pyblish.api.ContextPlugin):
+class CollectRemotePublishSettings(plugin.HoudiniContextPlugin):
"""Collect custom settings of the Remote Publish node."""
order = pyblish.api.CollectorOrder
families = ["*"]
- hosts = ["houdini"]
targets = ["deadline"]
label = "Remote Publish Submission Settings"
actions = [RepairAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_render_products.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_render_products.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_render_products.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_render_products.py
index fcd80e0082..e84f6c6f84 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_render_products.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_render_products.py
@@ -5,6 +5,7 @@ import hou
import pxr.UsdRender
import pyblish.api
+from ayon_houdini.api import plugin
def get_var_changed(variable=None):
@@ -41,12 +42,11 @@ def get_var_changed(variable=None):
return changed
-class CollectRenderProducts(pyblish.api.InstancePlugin):
+class CollectRenderProducts(plugin.HoudiniInstancePlugin):
"""Collect USD Render Products."""
label = "Collect Render Products"
order = pyblish.api.CollectorOrder + 0.4
- hosts = ["houdini"]
families = ["usdrender"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_review_data.py
similarity index 64%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_review_data.py
index 9671945b9a..cca55463e6 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_review_data.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_review_data.py
@@ -1,15 +1,16 @@
import hou
import pyblish.api
+from ayon_houdini.api import plugin
-class CollectHoudiniReviewData(pyblish.api.InstancePlugin):
+class CollectHoudiniReviewData(plugin.HoudiniInstancePlugin):
"""Collect Review Data."""
label = "Collect Review Data"
# This specific order value is used so that
# this plugin runs after CollectRopFrameRange
- order = pyblish.api.CollectorOrder + 0.1
- hosts = ["houdini"]
+ # Also after CollectLocalRenderInstances
+ order = pyblish.api.CollectorOrder + 0.13
families = ["review"]
def process(self, instance):
@@ -28,7 +29,8 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin):
ropnode_path = instance.data["instance_node"]
ropnode = hou.node(ropnode_path)
- camera_path = ropnode.parm("camera").eval()
+ # Get camera based on the instance_node type.
+ camera_path = self._get_camera_path(ropnode)
camera_node = hou.node(camera_path)
if not camera_node:
self.log.warning("No valid camera node found on review node: "
@@ -55,3 +57,29 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin):
# Store focal length in `burninDataMembers`
burnin_members = instance.data.setdefault("burninDataMembers", {})
burnin_members["focalLength"] = focal_length
+
+ def _get_camera_path(self, ropnode):
+ """Get the camera path associated with the given rop node.
+
+ This function evaluates the camera parameter according to the
+ type of the given rop node.
+
+ Returns:
+ Union[str, None]: Camera path or None.
+
+ This function can return empty string if the camera
+ path is empty i.e. no camera path.
+ """
+
+ if ropnode.type().name() in {
+ "opengl", "karma", "ifd", "arnold"
+ }:
+ return ropnode.parm("camera").eval()
+
+ elif ropnode.type().name() == "Redshift_ROP":
+ return ropnode.parm("RS_renderCamera").eval()
+
+ elif ropnode.type().name() == "vray_renderer":
+ return ropnode.parm("render_camera").eval()
+
+ return None
diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_reviewable_instances.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_reviewable_instances.py
new file mode 100644
index 0000000000..84cd8377a8
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_reviewable_instances.py
@@ -0,0 +1,23 @@
+import pyblish.api
+from ayon_houdini.api import plugin
+
+
+class CollectReviewableInstances(plugin.HoudiniInstancePlugin):
+ """Collect Reviewable Instances.
+
+ Basically, all instances of the specified families
+ with creator_attribure["review"]
+ """
+
+ order = pyblish.api.CollectorOrder
+ label = "Collect Reviewable Instances"
+ families = ["mantra_rop",
+ "karma_rop",
+ "redshift_rop",
+ "arnold_rop",
+ "vray_rop"]
+
+ def process(self, instance):
+ creator_attribute = instance.data["creator_attributes"]
+
+ instance.data["review"] = creator_attribute.get("review", False)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_rop_frame_range.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_rop_frame_range.py
similarity index 88%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_rop_frame_range.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_rop_frame_range.py
index 44afaf2466..c0f8d7aef9 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_rop_frame_range.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_rop_frame_range.py
@@ -2,13 +2,12 @@
"""Collector plugin for frames data on ROP instances."""
import hou # noqa
import pyblish.api
-from ayon_core.hosts.houdini.api import lib
+from ayon_houdini.api import lib, plugin
-class CollectRopFrameRange(pyblish.api.InstancePlugin):
+class CollectRopFrameRange(plugin.HoudiniInstancePlugin):
"""Collect all frames which would be saved from the ROP nodes"""
- hosts = ["houdini"]
order = pyblish.api.CollectorOrder
label = "Collect RopNode Frame Range"
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_staticmesh_type.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_staticmesh_type.py
similarity index 84%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_staticmesh_type.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_staticmesh_type.py
index db9efec7a1..1aab655532 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_staticmesh_type.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_staticmesh_type.py
@@ -2,12 +2,12 @@
"""Collector for staticMesh types. """
import pyblish.api
+from ayon_houdini.api import plugin
-class CollectStaticMeshType(pyblish.api.InstancePlugin):
+class CollectStaticMeshType(plugin.HoudiniInstancePlugin):
"""Collect data type for fbx instance."""
- hosts = ["houdini"]
families = ["staticMesh"]
label = "Collect type of staticMesh"
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_usd_bootstrap.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_usd_bootstrap.py
index cd82f1679a..5067b9aab2 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_bootstrap.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_usd_bootstrap.py
@@ -1,10 +1,10 @@
import pyblish.api
import ayon_api
-
from ayon_core.pipeline import usdlib, KnownPublishError
+from ayon_houdini.api import plugin
-class CollectUsdBootstrap(pyblish.api.InstancePlugin):
+class CollectUsdBootstrap(plugin.HoudiniInstancePlugin):
"""Collect special Asset/Shot bootstrap instances if those are needed.
Some specific products are intended to be part of the default structure
@@ -21,7 +21,6 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.35
label = "Collect USD Bootstrap"
- hosts = ["houdini"]
families = ["usd", "usd.layered"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_usd_layers.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_usd_layers.py
index 93add6806e..7ecf5fbb02 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_usd_layers.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_usd_layers.py
@@ -1,17 +1,15 @@
import os
-
-import pyblish.api
-import ayon_core.hosts.houdini.api.usd as usdlib
-
import hou
+import pyblish.api
+from ayon_houdini.api import plugin
+import ayon_houdini.api.usd as usdlib
-class CollectUsdLayers(pyblish.api.InstancePlugin):
+class CollectUsdLayers(plugin.HoudiniInstancePlugin):
"""Collect the USD Layers that have configured save paths."""
order = pyblish.api.CollectorOrder + 0.35
label = "Collect USD Layers"
- hosts = ["houdini"]
families = ["usd"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_vray_rop.py
similarity index 89%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_vray_rop.py
index 62b7dcdd5d..2f9c2bb18e 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_vray_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_vray_rop.py
@@ -4,16 +4,17 @@ import os
import hou
import pyblish.api
-from ayon_core.hosts.houdini.api.lib import (
+from ayon_houdini.api.lib import (
evalParmNoFrame,
get_color_management_preferences
)
-from ayon_core.hosts.houdini.api import (
- colorspace
+from ayon_houdini.api import (
+ colorspace,
+ plugin
)
-class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
+class CollectVrayROPRenderProducts(plugin.HoudiniInstancePlugin):
"""Collect Vray Render Products
Collects the instance.data["files"] for the render products.
@@ -27,7 +28,6 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
# This specific order value is used so that
# this plugin runs after CollectFrames
order = pyblish.api.CollectorOrder + 0.11
- hosts = ["houdini"]
families = ["vray_rop"]
def process(self, instance):
@@ -45,12 +45,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
render_products = []
# TODO: add render elements if render element
- # Store whether we are splitting the render job in an export + render
- split_render = rop.parm("render_export_mode").eval() == "2"
- instance.data["splitRender"] = split_render
export_prefix = None
export_products = []
- if split_render:
+ if instance.data["splitRender"]:
export_prefix = evalParmNoFrame(
rop, "render_export_filepath", pad_character="0"
)
@@ -70,6 +67,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
"": self.generate_expected_files(instance,
beauty_product)}
+ # Assume it's a multipartExr Render.
+ multipartExr = True
+
if instance.data.get("RenderElement", True):
render_element = self.get_render_element_name(rop, default_prefix)
if render_element:
@@ -77,7 +77,13 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
render_products.append(renderpass)
files_by_aov[aov] = self.generate_expected_files(
instance, renderpass)
+ # Set to False as soon as we have a separated aov.
+ multipartExr = False
+ # Review Logic expects this key to exist and be True
+ # if render is a multipart Exr.
+ # As long as we have one AOV then multipartExr should be True.
+ instance.data["multipartExr"] = multipartExr
for product in render_products:
self.log.debug("Found render product: %s" % product)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_workfile.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_workfile.py
similarity index 91%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_workfile.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_workfile.py
index aa533bcf1b..8d0939a803 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_workfile.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_workfile.py
@@ -1,14 +1,13 @@
import os
import pyblish.api
+from ayon_houdini.api import plugin
-
-class CollectWorkfile(pyblish.api.InstancePlugin):
+class CollectWorkfile(plugin.HoudiniInstancePlugin):
"""Inject workfile representation into instance"""
order = pyblish.api.CollectorOrder - 0.01
label = "Houdini Workfile Data"
- hosts = ["houdini"]
families = ["workfile"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/collect_workscene_fps.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_workscene_fps.py
similarity index 75%
rename from client/ayon_core/hosts/houdini/plugins/publish/collect_workscene_fps.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/collect_workscene_fps.py
index 6f6cc978cd..0091eb0abb 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/collect_workscene_fps.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_workscene_fps.py
@@ -1,13 +1,13 @@
-import pyblish.api
import hou
+import pyblish.api
+from ayon_houdini.api import plugin
-class CollectWorksceneFPS(pyblish.api.ContextPlugin):
+class CollectWorksceneFPS(plugin.HoudiniContextPlugin):
"""Get the FPS of the work scene."""
label = "Workscene FPS"
order = pyblish.api.CollectorOrder
- hosts = ["houdini"]
def process(self, context):
fps = hou.fps()
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_active_view_thumbnail.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_active_view_thumbnail.py
similarity index 88%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_active_view_thumbnail.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_active_view_thumbnail.py
index aedcb1da02..c4d51c0808 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_active_view_thumbnail.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_active_view_thumbnail.py
@@ -1,11 +1,10 @@
-import pyblish.api
import tempfile
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api import lib
-from ayon_core.hosts.houdini.api.pipeline import IS_HEADLESS
+import pyblish.api
+from ayon_houdini.api import lib, plugin
+from ayon_houdini.api.pipeline import IS_HEADLESS
-class ExtractActiveViewThumbnail(publish.Extractor):
+class ExtractActiveViewThumbnail(plugin.HoudiniExtractorPlugin):
"""Set instance thumbnail to a screengrab of current active viewport.
This makes it so that if an instance does not have a thumbnail set yet that
@@ -16,7 +15,6 @@ class ExtractActiveViewThumbnail(publish.Extractor):
order = pyblish.api.ExtractorOrder + 0.49
label = "Extract Active View Thumbnail"
families = ["workfile"]
- hosts = ["houdini"]
def process(self, instance):
if IS_HEADLESS:
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_alembic.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py
similarity index 70%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_alembic.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py
index daf30b26ed..e82f07284a 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_alembic.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py
@@ -1,18 +1,16 @@
import os
+import hou
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
-
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
-class ExtractAlembic(publish.Extractor):
+class ExtractAlembic(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder
label = "Extract Alembic"
- hosts = ["houdini"]
families = ["abc", "camera"]
targets = ["local", "remote"]
@@ -28,10 +26,15 @@ class ExtractAlembic(publish.Extractor):
staging_dir = os.path.dirname(output)
instance.data["stagingDir"] = staging_dir
- file_name = os.path.basename(output)
+ if instance.data.get("frames"):
+ # list of files
+ files = instance.data["frames"]
+ else:
+ # single file
+ files = os.path.basename(output)
# We run the render
- self.log.info("Writing alembic '%s' to '%s'" % (file_name,
+ self.log.info("Writing alembic '%s' to '%s'" % (files,
staging_dir))
render_rop(ropnode)
@@ -42,7 +45,7 @@ class ExtractAlembic(publish.Extractor):
representation = {
'name': 'abc',
'ext': 'abc',
- 'files': file_name,
+ 'files': files,
"stagingDir": staging_dir,
}
instance.data["representations"].append(representation)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_ass.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_ass.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py
index 24b956ad81..a796bbf4b3 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_ass.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py
@@ -1,19 +1,17 @@
import os
+import hou
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
-
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
-class ExtractAss(publish.Extractor):
+class ExtractAss(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder + 0.1
label = "Extract Ass"
families = ["ass"]
- hosts = ["houdini"]
targets = ["local", "remote"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_bgeo.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py
similarity index 85%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_bgeo.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py
index 448cf97848..ab8837065d 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_bgeo.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py
@@ -1,19 +1,15 @@
import os
+import hou
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
-from ayon_core.hosts.houdini.api import lib
-
-import hou
+from ayon_houdini.api import lib, plugin
-class ExtractBGEO(publish.Extractor):
+class ExtractBGEO(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder
label = "Extract BGEO"
- hosts = ["houdini"]
families = ["bgeo"]
def process(self, instance):
@@ -32,7 +28,7 @@ class ExtractBGEO(publish.Extractor):
file_name, staging_dir))
# write files
- render_rop(ropnode)
+ lib.render_rop(ropnode)
output = instance.data["frames"]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_composite.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_composite.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py
index 0fab69ef4a..cab462aef6 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_composite.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py
@@ -1,18 +1,17 @@
import os
+import hou
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop, splitext
-
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop, splitext
-class ExtractComposite(publish.Extractor,
+class ExtractComposite(plugin.HoudiniExtractorPlugin,
publish.ColormanagedPyblishPluginMixin):
order = pyblish.api.ExtractorOrder
label = "Extract Composite (Image Sequence)"
- hosts = ["houdini"]
families = ["imagesequence"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_fbx.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_fbx.py
similarity index 90%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_fbx.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_fbx.py
index 7ef004d7cb..49b3fa07ca 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_fbx.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_fbx.py
@@ -2,18 +2,16 @@
"""Fbx Extractor for houdini. """
import os
-import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
-
import hou
+import pyblish.api
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
-class ExtractFBX(publish.Extractor):
+class ExtractFBX(plugin.HoudiniExtractorPlugin):
label = "Extract FBX"
families = ["fbx"]
- hosts = ["houdini"]
order = pyblish.api.ExtractorOrder + 0.1
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_hda.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_hda.py
index 5fe83e0dcf..e4449d11f8 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_hda.py
@@ -1,16 +1,15 @@
# -*- coding: utf-8 -*-
import os
from pprint import pformat
-import pyblish.api
-from ayon_core.pipeline import publish
import hou
+import pyblish.api
+from ayon_houdini.api import plugin
-class ExtractHDA(publish.Extractor):
+class ExtractHDA(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder
label = "Extract HDA"
- hosts = ["houdini"]
families = ["hda"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_ifd.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_ifd.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py
index f0bcf4b371..b424f2e452 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_mantra_ifd.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py
@@ -1,17 +1,15 @@
import os
+import hou
import pyblish.api
-from ayon_core.pipeline import publish
-
-import hou
+from ayon_houdini.api import plugin
-class ExtractMantraIFD(publish.Extractor):
+class ExtractMantraIFD(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder
label = "Extract Mantra ifd"
- hosts = ["houdini"]
families = ["mantraifd"]
targets = ["local", "remote"]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py
similarity index 73%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py
index 57bb8b881a..bee1bf871f 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_opengl.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py
@@ -1,24 +1,33 @@
import os
+import hou
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
-
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
-class ExtractOpenGL(publish.Extractor,
+class ExtractOpenGL(plugin.HoudiniExtractorPlugin,
publish.ColormanagedPyblishPluginMixin):
order = pyblish.api.ExtractorOrder - 0.01
label = "Extract OpenGL"
families = ["review"]
- hosts = ["houdini"]
def process(self, instance):
ropnode = hou.node(instance.data.get("instance_node"))
+ # This plugin is triggered when marking render as reviewable.
+ # Therefore, this plugin will run on over wrong instances.
+ # TODO: Don't run this plugin on wrong instances.
+ # This plugin should run only on review product type
+ # with instance node of opengl type.
+ if ropnode.type().name() != "opengl":
+ self.log.debug("Skipping OpenGl extraction. Rop node {} "
+ "is not an OpenGl node.".format(ropnode.path()))
+ return
+
output = ropnode.evalParm("picture")
staging_dir = os.path.normpath(os.path.dirname(output))
instance.data["stagingDir"] = staging_dir
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_redshift_proxy.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py
similarity index 90%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_redshift_proxy.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py
index e08a73ae8e..3e8a79df00 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_redshift_proxy.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py
@@ -1,19 +1,17 @@
import os
+import hou
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
-
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
-class ExtractRedshiftProxy(publish.Extractor):
+class ExtractRedshiftProxy(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder + 0.1
label = "Extract Redshift Proxy"
families = ["redshiftproxy"]
- hosts = ["houdini"]
targets = ["local", "remote"]
def process(self, instance):
diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_render.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_render.py
new file mode 100644
index 0000000000..8ff8590650
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_render.py
@@ -0,0 +1,74 @@
+import os
+import hou
+
+import pyblish.api
+
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
+
+
+class ExtractRender(plugin.HoudiniExtractorPlugin):
+
+ order = pyblish.api.ExtractorOrder
+ label = "Extract Render"
+ families = ["mantra_rop",
+ "karma_rop",
+ "redshift_rop",
+ "arnold_rop",
+ "vray_rop"]
+
+ def process(self, instance):
+ creator_attribute = instance.data["creator_attributes"]
+ product_type = instance.data["productType"]
+ rop_node = hou.node(instance.data.get("instance_node"))
+
+ # Align split parameter value on rop node to the render target.
+ if instance.data["splitRender"]:
+ if product_type == "arnold_rop":
+ rop_node.setParms({"ar_ass_export_enable": 1})
+ elif product_type == "mantra_rop":
+ rop_node.setParms({"soho_outputmode": 1})
+ elif product_type == "redshift_rop":
+ rop_node.setParms({"RS_archive_enable": 1})
+ elif product_type == "vray_rop":
+ rop_node.setParms({"render_export_mode": "2"})
+ else:
+ if product_type == "arnold_rop":
+ rop_node.setParms({"ar_ass_export_enable": 0})
+ elif product_type == "mantra_rop":
+ rop_node.setParms({"soho_outputmode": 0})
+ elif product_type == "redshift_rop":
+ rop_node.setParms({"RS_archive_enable": 0})
+ elif product_type == "vray_rop":
+ rop_node.setParms({"render_export_mode": "1"})
+
+ if instance.data.get("farm"):
+ self.log.debug("Render should be processed on farm, skipping local render.")
+ return
+
+ if creator_attribute.get("render_target") == "local":
+ ropnode = hou.node(instance.data.get("instance_node"))
+ render_rop(ropnode)
+
+ # `ExpectedFiles` is a list that includes one dict.
+ expected_files = instance.data["expectedFiles"][0]
+ # Each key in that dict is a list of files.
+ # Combine lists of files into one big list.
+ all_frames = []
+ for value in expected_files.values():
+ if isinstance(value, str):
+ all_frames.append(value)
+ elif isinstance(value, list):
+ all_frames.extend(value)
+ # Check missing frames.
+ # Frames won't exist if user cancels the render.
+ missing_frames = [
+ frame
+ for frame in all_frames
+ if not os.path.exists(frame)
+ ]
+ if missing_frames:
+ # TODO: Use user friendly error reporting.
+ raise RuntimeError("Failed to complete render extraction. "
+ "Missing output files: {}".format(
+ missing_frames))
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_usd.py
similarity index 87%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_usd.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_usd.py
index 0aeed06643..40e6211e63 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_usd.py
@@ -1,17 +1,16 @@
import os
+import hou
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
-import hou
-class ExtractUSD(publish.Extractor):
+class ExtractUSD(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder
label = "Extract USD"
- hosts = ["houdini"]
families = ["usd",
"usdModel",
"usdSetDress"]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_usd_layered.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_usd_layered.py
index 2e5c9a892c..6a377c57cf 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_usd_layered.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_usd_layered.py
@@ -7,12 +7,10 @@ import hou
import ayon_api
import pyblish.api
-from ayon_core.pipeline import (
- get_representation_path,
- publish,
-)
-import ayon_core.hosts.houdini.api.usd as hou_usdlib
-from ayon_core.hosts.houdini.api.lib import render_rop
+from ayon_core.pipeline import get_representation_path
+from ayon_houdini.api import plugin
+import ayon_houdini.api.usd as hou_usdlib
+from ayon_houdini.api.lib import render_rop
class ExitStack(object):
@@ -154,11 +152,10 @@ def parm_values(overrides):
parm.set(value)
-class ExtractUSDLayered(publish.Extractor):
+class ExtractUSDLayered(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder
label = "Extract Layered USD"
- hosts = ["houdini"]
families = ["usdLayered", "usdShade"]
# Force Output Processors so it will always save any file
@@ -312,3 +309,14 @@ class ExtractUSDLayered(publish.Extractor):
return False
return filecmp.cmp(old_file, new_file)
+
+ def staging_dir(self, instance):
+ """Provide a temporary directory in which to store extracted files
+
+ Upon calling this method the staging directory is stored inside
+ the instance.data['stagingDir']
+ """
+
+ from ayon_core.pipeline.publish import get_instance_staging_dir
+
+ return get_instance_staging_dir(instance)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_vdb_cache.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py
similarity index 89%
rename from client/ayon_core/hosts/houdini/plugins/publish/extract_vdb_cache.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py
index 4544d33e57..a944d81e9b 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/extract_vdb_cache.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py
@@ -1,19 +1,17 @@
import os
+import hou
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.houdini.api.lib import render_rop
-
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import render_rop
-class ExtractVDBCache(publish.Extractor):
+class ExtractVDBCache(plugin.HoudiniExtractorPlugin):
order = pyblish.api.ExtractorOrder + 0.1
label = "Extract VDB Cache"
families = ["vdbcache"]
- hosts = ["houdini"]
def process(self, instance):
if instance.data.get("farm"):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml b/server_addon/houdini/client/ayon_houdini/plugins/publish/help/validate_vdb_output_node.xml
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/help/validate_vdb_output_node.xml
index eb83bfffe3..8aac9a6a07 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/help/validate_vdb_output_node.xml
@@ -25,4 +25,4 @@ ROP node `{rop_path}` is set to export SOP path `{sop_path}`.
-
\ No newline at end of file
+
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/increment_current_file.py
similarity index 83%
rename from client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/increment_current_file.py
index fe8fa25f10..878500f605 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/increment_current_file.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/increment_current_file.py
@@ -2,11 +2,15 @@ import pyblish.api
from ayon_core.lib import version_up
from ayon_core.pipeline import registered_host
-from ayon_core.pipeline.publish import get_errored_plugins_from_context
-from ayon_core.pipeline.publish import KnownPublishError
+from ayon_core.pipeline.publish import (
+ get_errored_plugins_from_context,
+ KnownPublishError
+)
+
+from ayon_houdini.api import plugin
-class IncrementCurrentFile(pyblish.api.ContextPlugin):
+class IncrementCurrentFile(plugin.HoudiniContextPlugin):
"""Increment the current file.
Saves the current scene with an increased version number.
@@ -15,13 +19,14 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin):
label = "Increment current file"
order = pyblish.api.IntegratorOrder + 9.0
- hosts = ["houdini"]
families = ["workfile",
- "redshift_rop",
- "arnold_rop",
+ "usdrender",
"mantra_rop",
"karma_rop",
- "usdrender",
+ "redshift_rop",
+ "arnold_rop",
+ "vray_rop",
+ "render.local.hou",
"publish.hou"]
optional = True
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/save_scene.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/save_scene.py
similarity index 89%
rename from client/ayon_core/hosts/houdini/plugins/publish/save_scene.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/save_scene.py
index 7c453038ea..e0734da5d1 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/save_scene.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/save_scene.py
@@ -2,13 +2,14 @@ import pyblish.api
from ayon_core.pipeline import registered_host
+from ayon_houdini.api import plugin
-class SaveCurrentScene(pyblish.api.ContextPlugin):
+
+class SaveCurrentScene(plugin.HoudiniContextPlugin):
"""Save current scene"""
label = "Save current file"
order = pyblish.api.ExtractorOrder - 0.49
- hosts = ["houdini"]
def process(self, context):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_abc_primitive_to_detail.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_abc_primitive_to_detail.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_abc_primitive_to_detail.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_abc_primitive_to_detail.py
index 18a459bf7b..51885a963e 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_abc_primitive_to_detail.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_abc_primitive_to_detail.py
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
-import pyblish.api
-
from collections import defaultdict
+
+import pyblish.api
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
-class ValidateAbcPrimitiveToDetail(pyblish.api.InstancePlugin):
+
+class ValidateAbcPrimitiveToDetail(plugin.HoudiniInstancePlugin):
"""Validate Alembic ROP Primitive to Detail attribute is consistent.
The Alembic ROP crashes Houdini whenever an attribute in the "Primitive to
@@ -18,7 +20,6 @@ class ValidateAbcPrimitiveToDetail(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder + 0.1
families = ["abc"]
- hosts = ["houdini"]
label = "Validate Primitive to Detail (Abc)"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_alembic_face_sets.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_alembic_face_sets.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_alembic_face_sets.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_alembic_face_sets.py
index 40114bc40e..00ce554ff1 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_alembic_face_sets.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_alembic_face_sets.py
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
-import pyblish.api
import hou
+import pyblish.api
+from ayon_houdini.api import plugin
-class ValidateAlembicROPFaceSets(pyblish.api.InstancePlugin):
+
+class ValidateAlembicROPFaceSets(plugin.HoudiniInstancePlugin):
"""Validate Face Sets are disabled for extraction to pointcache.
When groups are saved as Face Sets with the Alembic these show up
@@ -19,7 +21,6 @@ class ValidateAlembicROPFaceSets(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder + 0.1
families = ["abc"]
- hosts = ["houdini"]
label = "Validate Alembic ROP Face Sets"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_alembic_input_node.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_alembic_input_node.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_alembic_input_node.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_alembic_input_node.py
index dbc38058e6..aab3068171 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_alembic_input_node.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_alembic_input_node.py
@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
+import hou
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-import hou
+
+from ayon_houdini.api import plugin
-class ValidateAlembicInputNode(pyblish.api.InstancePlugin):
+class ValidateAlembicInputNode(plugin.HoudiniInstancePlugin):
"""Validate that the node connected to the output is correct.
The connected node cannot be of the following types for Alembic:
@@ -15,7 +17,6 @@ class ValidateAlembicInputNode(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder + 0.1
families = ["abc"]
- hosts = ["houdini"]
label = "Validate Input Node (Abc)"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_animation_settings.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_animation_settings.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_animation_settings.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_animation_settings.py
index e28c38ece0..1cc9e24dc9 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_animation_settings.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_animation_settings.py
@@ -1,11 +1,12 @@
-import pyblish.api
-
-from ayon_core.pipeline.publish import PublishValidationError
-from ayon_core.hosts.houdini.api import lib
import hou
+import pyblish.api
+from ayon_core.pipeline.publish import PublishValidationError
-class ValidateAnimationSettings(pyblish.api.InstancePlugin):
+from ayon_houdini.api import lib, plugin
+
+
+class ValidateAnimationSettings(plugin.HoudiniInstancePlugin):
"""Validate if the unexpanded string contains the frame ('$F') token
This validator will only check the output parameter of the node if
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_bypass.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_bypass.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_bypass.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_bypass.py
index 8a83ff42fb..f3856b4147 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_bypass.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_bypass.py
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
+import hou
+
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-import hou
+from ayon_houdini.api import plugin
-class ValidateBypassed(pyblish.api.InstancePlugin):
+
+class ValidateBypassed(plugin.HoudiniInstancePlugin):
"""Validate all primitives build hierarchy from attribute when enabled.
The name of the attribute must exist on the prims and have the same name
@@ -15,7 +18,6 @@ class ValidateBypassed(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder - 0.1
families = ["*"]
- hosts = ["houdini"]
label = "Validate ROP Bypass"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_camera_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_camera_rop.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_camera_rop.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_camera_rop.py
index ad4ace988a..f21addb11d 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_camera_rop.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_camera_rop.py
@@ -3,13 +3,14 @@
import pyblish.api
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
-class ValidateCameraROP(pyblish.api.InstancePlugin):
+
+class ValidateCameraROP(plugin.HoudiniInstancePlugin):
"""Validate Camera ROP settings."""
order = pyblish.api.ValidatorOrder
families = ["camera"]
- hosts = ["houdini"]
label = "Camera ROP"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_cop_output_node.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_cop_output_node.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_cop_output_node.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_cop_output_node.py
index 91bd36018a..1d63e15d90 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_cop_output_node.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_cop_output_node.py
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
import hou
-import pyblish.api
+import pyblish.api
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
-class ValidateCopOutputNode(pyblish.api.InstancePlugin):
+
+class ValidateCopOutputNode(plugin.HoudiniInstancePlugin):
"""Validate the instance COP Output Node.
This will ensure:
@@ -17,7 +19,6 @@ class ValidateCopOutputNode(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["imagesequence"]
- hosts = ["houdini"]
label = "Validate COP Output Node"
def process(self, instance):
diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_export_is_a_single_frame.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_export_is_a_single_frame.py
new file mode 100644
index 0000000000..b26c60320b
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_export_is_a_single_frame.py
@@ -0,0 +1,59 @@
+# -*- 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_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"]
+ 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
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_fbx_output_node.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_fbx_output_node.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_fbx_output_node.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_fbx_output_node.py
index 08eaa182c0..1c236bb8f7 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_fbx_output_node.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_fbx_output_node.py
@@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.houdini.api.action import (
+from ayon_houdini.api.action import (
SelectInvalidAction,
SelectROPAction,
)
-from ayon_core.hosts.houdini.api.lib import get_obj_node_output
+from ayon_houdini.api import plugin
+from ayon_houdini.api.lib import get_obj_node_output
import hou
-class ValidateFBXOutputNode(pyblish.api.InstancePlugin):
+class ValidateFBXOutputNode(plugin.HoudiniInstancePlugin):
"""Validate the instance Output Node.
This will ensure:
@@ -22,7 +23,6 @@ class ValidateFBXOutputNode(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["fbx"]
- hosts = ["houdini"]
label = "Validate FBX Output Node"
actions = [SelectROPAction, SelectInvalidAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_file_extension.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_file_extension.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_file_extension.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_file_extension.py
index e9a0397a58..1b3a58f4b3 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_file_extension.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_file_extension.py
@@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
import os
-import pyblish.api
-
-from ayon_core.hosts.houdini.api import lib
-from ayon_core.pipeline import PublishValidationError
-
import hou
+import pyblish.api
+from ayon_core.pipeline import PublishValidationError
-class ValidateFileExtension(pyblish.api.InstancePlugin):
+from ayon_houdini.api import lib, plugin
+
+
+class ValidateFileExtension(plugin.HoudiniInstancePlugin):
"""Validate the output file extension fits the output family.
File extensions:
@@ -20,7 +20,6 @@ class ValidateFileExtension(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["camera", "vdbcache"]
- hosts = ["houdini"]
label = "Output File Extension"
family_extensions = {
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_range.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_frame_range.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_frame_range.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_frame_range.py
index 2a3418ee7e..9435fa033a 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_range.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_frame_range.py
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
+import hou
+
import pyblish.api
from ayon_core.pipeline import PublishValidationError
from ayon_core.pipeline.publish import RepairAction
-from ayon_core.hosts.houdini.api.action import SelectInvalidAction
-import hou
+from ayon_houdini.api.action import SelectInvalidAction
+from ayon_houdini.api import plugin
+
class DisableUseFolderHandlesAction(RepairAction):
@@ -12,7 +15,7 @@ class DisableUseFolderHandlesAction(RepairAction):
icon = "mdi.toggle-switch-off"
-class ValidateFrameRange(pyblish.api.InstancePlugin):
+class ValidateFrameRange(plugin.HoudiniInstancePlugin):
"""Validate Frame Range.
Due to the usage of start and end handles,
@@ -21,7 +24,6 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
"""
order = pyblish.api.ValidatorOrder - 0.1
- hosts = ["houdini"]
label = "Validate Frame Range"
actions = [DisableUseFolderHandlesAction, SelectInvalidAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_token.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_frame_token.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_frame_token.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_frame_token.py
index 20fb859146..46c02ba6f2 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_frame_token.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_frame_token.py
@@ -1,10 +1,11 @@
-import pyblish.api
-
-from ayon_core.hosts.houdini.api import lib
import hou
+import pyblish.api
-class ValidateFrameToken(pyblish.api.InstancePlugin):
+from ayon_houdini.api import lib, plugin
+
+
+class ValidateFrameToken(plugin.HoudiniInstancePlugin):
"""Validate if the unexpanded string contains the frame ('$F') token.
This validator will *only* check the output parameter of the node if
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_houdini_license_category.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_houdini_license_category.py
similarity index 91%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_houdini_license_category.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_houdini_license_category.py
index 9a68c34405..d76f8a0072 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_houdini_license_category.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_houdini_license_category.py
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
-import pyblish.api
-from ayon_core.pipeline import PublishValidationError
import hou
+import pyblish.api
+from ayon_core.pipeline import PublishValidationError
-class ValidateHoudiniNotApprenticeLicense(pyblish.api.InstancePlugin):
+from ayon_houdini.api import plugin
+
+
+class ValidateHoudiniNotApprenticeLicense(plugin.HoudiniInstancePlugin):
"""Validate the Houdini instance runs a non Apprentice license.
USD ROPs:
@@ -21,7 +24,6 @@ class ValidateHoudiniNotApprenticeLicense(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["usd", "abc", "fbx", "camera"]
- hosts = ["houdini"]
label = "Houdini Apprentice License"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_instance_in_context.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_instance_in_context.py
index 26708e306b..7566dff240 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_instance_in_context.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_instance_in_context.py
@@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
"""Validate if instance asset is the same as context asset."""
-
-import pyblish.api
-from ayon_core.hosts.houdini.api.action import SelectROPAction
from ayon_core.pipeline.publish import (
RepairAction,
ValidateContentsOrder,
@@ -10,8 +7,11 @@ from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin
)
+from ayon_houdini.api import plugin
+from ayon_houdini.api.action import SelectROPAction
-class ValidateInstanceInContextHoudini(pyblish.api.InstancePlugin,
+
+class ValidateInstanceInContextHoudini(plugin.HoudiniInstancePlugin,
OptionalPyblishPluginMixin):
"""Validator to check if instance asset match context asset.
@@ -24,7 +24,6 @@ class ValidateInstanceInContextHoudini(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
label = "Instance in same Context"
optional = True
- hosts = ["houdini"]
actions = [SelectROPAction, RepairAction]
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_mesh_is_static.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_mesh_is_static.py
similarity index 77%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_mesh_is_static.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_mesh_is_static.py
index 289e00339b..b6725bc36c 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_mesh_is_static.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_mesh_is_static.py
@@ -1,25 +1,28 @@
# -*- coding: utf-8 -*-
"""Validator for correct naming of Static Meshes."""
-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
-from ayon_core.hosts.houdini.api.lib import get_output_children
+from ayon_houdini.api import plugin
+from ayon_houdini.api.action import SelectInvalidAction
+from ayon_houdini.api.lib import get_output_children
-class ValidateMeshIsStatic(pyblish.api.InstancePlugin,
+class ValidateMeshIsStatic(plugin.HoudiniInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate mesh is static.
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"]
- hosts = ["houdini"]
+ families = ["staticMesh",
+ "model"]
label = "Validate Mesh is Static"
order = ValidateContentsOrder + 0.1
actions = [SelectInvalidAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_mkpaths_toggled.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_mkpaths_toggled.py
similarity index 83%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_mkpaths_toggled.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_mkpaths_toggled.py
index 38f1c4e176..4573d4ba0b 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_mkpaths_toggled.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_mkpaths_toggled.py
@@ -1,14 +1,16 @@
# -*- coding: utf-8 -*-
import pyblish.api
+
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
-class ValidateIntermediateDirectoriesChecked(pyblish.api.InstancePlugin):
+
+class ValidateIntermediateDirectoriesChecked(plugin.HoudiniInstancePlugin):
"""Validate Create Intermediate Directories is enabled on ROP node."""
order = pyblish.api.ValidatorOrder
- families = ["pointcache", "camera", "vdbcache"]
- hosts = ["houdini"]
+ families = ["pointcache", "camera", "vdbcache", "model"]
label = "Create Intermediate Directories Checked"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_no_errors.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_no_errors.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_no_errors.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_no_errors.py
index ae1e5cad27..ef66665d7b 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_no_errors.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_no_errors.py
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
-import pyblish.api
import hou
+
+import pyblish.api
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
+
def cook_in_range(node, start, end):
current = hou.intFrame()
@@ -26,11 +29,10 @@ def get_errors(node):
return node.errors()
-class ValidateNoErrors(pyblish.api.InstancePlugin):
+class ValidateNoErrors(plugin.HoudiniInstancePlugin):
"""Validate the Instance has no current cooking errors."""
order = pyblish.api.ValidatorOrder
- hosts = ["houdini"]
label = "Validate no errors"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_primitive_hierarchy_paths.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_primitive_hierarchy_paths.py
similarity index 98%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_primitive_hierarchy_paths.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_primitive_hierarchy_paths.py
index f63cb23138..9daab2a1a3 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_primitive_hierarchy_paths.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_primitive_hierarchy_paths.py
@@ -1,20 +1,20 @@
# -*- coding: utf-8 -*-
-import pyblish.api
+import hou
+
+from ayon_houdini.api import plugin
from ayon_core.pipeline import PublishValidationError
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
RepairAction,
)
-import hou
-
class AddDefaultPathAction(RepairAction):
label = "Add a default path attribute"
icon = "mdi.pencil-plus-outline"
-class ValidatePrimitiveHierarchyPaths(pyblish.api.InstancePlugin):
+class ValidatePrimitiveHierarchyPaths(plugin.HoudiniInstancePlugin):
"""Validate all primitives build hierarchy from attribute when enabled.
The name of the attribute must exist on the prims and have the same name
@@ -25,7 +25,6 @@ class ValidatePrimitiveHierarchyPaths(pyblish.api.InstancePlugin):
order = ValidateContentsOrder + 0.1
families = ["abc"]
- hosts = ["houdini"]
label = "Validate Prims Hierarchy Path"
actions = [AddDefaultPathAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_remote_publish.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_remote_publish.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish.py
index 133b45e8c3..08597c0a6f 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_remote_publish.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish.py
@@ -1,19 +1,18 @@
# -*-coding: utf-8 -*-
-import pyblish.api
+import hou
-from ayon_core.hosts.houdini.api import lib
+import pyblish.api
from ayon_core.pipeline.publish import RepairContextAction
from ayon_core.pipeline import PublishValidationError
-import hou
+from ayon_houdini.api import lib, plugin
-class ValidateRemotePublishOutNode(pyblish.api.ContextPlugin):
+class ValidateRemotePublishOutNode(plugin.HoudiniContextPlugin):
"""Validate the remote publish out node exists for Deadline to trigger."""
order = pyblish.api.ValidatorOrder - 0.4
families = ["*"]
- hosts = ["houdini"]
targets = ["deadline"]
label = "Remote Publish ROP node"
actions = [RepairContextAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_remote_publish_enabled.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish_enabled.py
similarity index 91%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_remote_publish_enabled.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish_enabled.py
index d4c6e7a45e..dc5666609f 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_remote_publish_enabled.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish_enabled.py
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
-import pyblish.api
-
import hou
+
+import pyblish.api
from ayon_core.pipeline.publish import RepairContextAction
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
-class ValidateRemotePublishEnabled(pyblish.api.ContextPlugin):
+
+class ValidateRemotePublishEnabled(plugin.HoudiniContextPlugin):
"""Validate the remote publish node is *not* bypassed."""
order = pyblish.api.ValidatorOrder - 0.39
families = ["*"]
- hosts = ["houdini"]
targets = ["deadline"]
label = "Remote Publish ROP enabled"
actions = [RepairContextAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_review_colorspace.py
similarity index 84%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_review_colorspace.py
index e7f528ba57..e96b222446 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_review_colorspace.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_review_colorspace.py
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
+import os
+import hou
+
import pyblish.api
from ayon_core.pipeline import (
PublishValidationError,
@@ -9,10 +12,9 @@ from ayon_core.pipeline.publish import (
get_plugin_settings,
apply_plugin_settings_automatically
)
-from ayon_core.hosts.houdini.api.action import SelectROPAction
-import os
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.action import SelectROPAction
class ResetViewSpaceAction(RepairAction):
@@ -20,7 +22,7 @@ class ResetViewSpaceAction(RepairAction):
icon = "mdi.monitor"
-class ValidateReviewColorspace(pyblish.api.InstancePlugin,
+class ValidateReviewColorspace(plugin.HoudiniInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate Review Colorspace parameters.
@@ -29,7 +31,6 @@ class ValidateReviewColorspace(pyblish.api.InstancePlugin,
order = pyblish.api.ValidatorOrder + 0.1
families = ["review"]
- hosts = ["houdini"]
label = "Validate Review Colorspace"
actions = [ResetViewSpaceAction, SelectROPAction]
@@ -56,6 +57,18 @@ class ValidateReviewColorspace(pyblish.api.InstancePlugin,
def process(self, instance):
+ rop_node = hou.node(instance.data["instance_node"])
+
+ # This plugin is triggered when marking render as reviewable.
+ # Therefore, this plugin will run on over wrong instances.
+ # TODO: Don't run this plugin on wrong instances.
+ # This plugin should run only on review product type
+ # with instance node of opengl type.
+ if rop_node.type().name() != "opengl":
+ self.log.debug("Skipping Validation. Rop node {} "
+ "is not an OpenGl node.".format(rop_node.path()))
+ return
+
if not self.is_active(instance.data):
return
@@ -66,7 +79,6 @@ class ValidateReviewColorspace(pyblish.api.InstancePlugin,
)
return
- rop_node = hou.node(instance.data["instance_node"])
if rop_node.evalParm("colorcorrect") != 2:
# any colorspace settings other than default requires
# 'Color Correct' parm to be set to 'OpenColorIO'
@@ -112,14 +124,14 @@ class ValidateReviewColorspace(pyblish.api.InstancePlugin,
As if OCIO is disabled the whole validation is skipped
and this repair action won't show up.
"""
- from ayon_core.hosts.houdini.api.lib import set_review_color_space
+ from ayon_houdini.api.lib import set_review_color_space
# Fall to the default value if cls.review_color_space is empty.
if not cls.review_color_space:
# cls.review_color_space is an empty string
# when the imageio/workfile setting is disabled or
# when the Review colorspace setting is empty.
- from ayon_core.hosts.houdini.api.colorspace import get_default_display_view_colorspace # noqa
+ from ayon_houdini.api.colorspace import get_default_display_view_colorspace # noqa
cls.review_color_space = get_default_display_view_colorspace()
rop_node = hou.node(instance.data["instance_node"])
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_scene_review.py
similarity index 79%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_scene_review.py
index b6007d3f0f..f45cd1c97d 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_scene_review.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_scene_review.py
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
-import pyblish.api
-from ayon_core.pipeline import PublishValidationError
import hou
+import pyblish.api
+from ayon_core.pipeline import PublishValidationError
-class ValidateSceneReview(pyblish.api.InstancePlugin):
+from ayon_houdini.api import plugin
+
+
+class ValidateSceneReview(plugin.HoudiniInstancePlugin):
"""Validator Some Scene Settings before publishing the review
1. Scene Path
2. Resolution
@@ -12,7 +15,6 @@ class ValidateSceneReview(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["review"]
- hosts = ["houdini"]
label = "Scene Setting for review"
def process(self, instance):
@@ -20,6 +22,16 @@ class ValidateSceneReview(pyblish.api.InstancePlugin):
report = []
instance_node = hou.node(instance.data.get("instance_node"))
+ # This plugin is triggered when marking render as reviewable.
+ # Therefore, this plugin will run on over wrong instances.
+ # TODO: Don't run this plugin on wrong instances.
+ # This plugin should run only on review product type
+ # with instance node of opengl type.
+ if instance_node.type().name() != "opengl":
+ self.log.debug("Skipping Validation. Rop node {} "
+ "is not an OpenGl node.".format(instance_node.path()))
+ return
+
invalid = self.get_invalid_scene_path(instance_node)
if invalid:
report.append(invalid)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_sop_output_node.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_sop_output_node.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_sop_output_node.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_sop_output_node.py
index 61cf7596ac..7d37927058 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_sop_output_node.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_sop_output_node.py
@@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
+import hou
+
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-from ayon_core.hosts.houdini.api.action import (
+
+from ayon_houdini.api import plugin
+from ayon_houdini.api.action import (
SelectInvalidAction,
SelectROPAction,
)
-import hou
-
-class ValidateSopOutputNode(pyblish.api.InstancePlugin):
+class ValidateSopOutputNode(plugin.HoudiniInstancePlugin):
"""Validate the instance SOP Output Node.
This will ensure:
@@ -22,8 +24,7 @@ class ValidateSopOutputNode(pyblish.api.InstancePlugin):
"""
order = pyblish.api.ValidatorOrder
- families = ["pointcache", "vdbcache"]
- hosts = ["houdini"]
+ families = ["pointcache", "vdbcache", "model"]
label = "Validate Output Node (SOP)"
actions = [SelectROPAction, SelectInvalidAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py
index 0481929824..dfd353bddf 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
"""Validator for correct naming of Static Meshes."""
-import pyblish.api
+import hou
+
from ayon_core.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
@@ -9,24 +10,23 @@ from ayon_core.pipeline.publish import (
ValidateContentsOrder,
RepairAction,
)
-from ayon_core.hosts.houdini.api.action import SelectInvalidAction
-from ayon_core.pipeline.create import get_product_name
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.action import SelectInvalidAction
+from ayon_core.pipeline.create import get_product_name
class FixProductNameAction(RepairAction):
label = "Fix Product Name"
-class ValidateSubsetName(pyblish.api.InstancePlugin,
+class ValidateSubsetName(plugin.HoudiniInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate Product name.
"""
families = ["staticMesh"]
- hosts = ["houdini"]
label = "Validate Product Name"
order = ValidateContentsOrder + 0.1
actions = [FixProductNameAction, SelectInvalidAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_unreal_staticmesh_naming.py
similarity index 91%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_unreal_staticmesh_naming.py
index ae00bc9db4..a3d971695d 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_unreal_staticmesh_naming.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_unreal_staticmesh_naming.py
@@ -1,19 +1,19 @@
# -*- coding: utf-8 -*-
"""Validator for correct naming of Static Meshes."""
-import pyblish.api
+import hou
+
from ayon_core.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
)
from ayon_core.pipeline.publish import ValidateContentsOrder
-from ayon_core.hosts.houdini.api.action import SelectInvalidAction
-from ayon_core.hosts.houdini.api.lib import get_output_children
-
-import hou
+from ayon_houdini.api import plugin
+from ayon_houdini.api.action import SelectInvalidAction
+from ayon_houdini.api.lib import get_output_children
-class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin,
+class ValidateUnrealStaticMeshName(plugin.HoudiniInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate name of Unreal Static Mesh.
@@ -29,7 +29,6 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin,
"""
families = ["staticMesh"]
- hosts = ["houdini"]
label = "Unreal Static Mesh Name (FBX)"
order = ValidateContentsOrder + 0.1
actions = [SelectInvalidAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_layer_path_backslashes.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_layer_path_backslashes.py
similarity index 91%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_usd_layer_path_backslashes.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_layer_path_backslashes.py
index 2b727670ad..4da67ff199 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_layer_path_backslashes.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_layer_path_backslashes.py
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
-import pyblish.api
-
-import ayon_core.hosts.houdini.api.usd as hou_usdlib
-from ayon_core.pipeline import PublishValidationError
-
import hou
+import pyblish.api
+from ayon_core.pipeline import PublishValidationError
-class ValidateUSDLayerPathBackslashes(pyblish.api.InstancePlugin):
+from ayon_houdini.api import plugin
+import ayon_houdini.api.usd as hou_usdlib
+
+
+class ValidateUSDLayerPathBackslashes(plugin.HoudiniInstancePlugin):
"""Validate USD loaded paths have no backslashes.
This is a crucial validation for HUSK USD rendering as Houdini's
@@ -22,7 +23,6 @@ class ValidateUSDLayerPathBackslashes(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["usdSetDress", "usdShade", "usd", "usdrender"]
- hosts = ["houdini"]
label = "USD Layer path backslashes"
optional = True
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_model_and_shade.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_model_and_shade.py
similarity index 93%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_usd_model_and_shade.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_model_and_shade.py
index dc1a19cae0..935bd39e23 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_model_and_shade.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_model_and_shade.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
-import pyblish.api
-
-import ayon_core.hosts.houdini.api.usd as hou_usdlib
-from ayon_core.pipeline import PublishValidationError
-
+import hou
from pxr import UsdShade, UsdRender, UsdLux
-import hou
+import pyblish.api
+from ayon_core.pipeline import PublishValidationError
+
+from ayon_houdini.api import plugin
+import ayon_houdini.api.usd as hou_usdlib
def fullname(o):
@@ -17,7 +17,7 @@ def fullname(o):
return module + "." + o.__name__
-class ValidateUsdModel(pyblish.api.InstancePlugin):
+class ValidateUsdModel(plugin.HoudiniInstancePlugin):
"""Validate USD Model.
Disallow Shaders, Render settings, products and vars and Lux lights.
@@ -26,7 +26,6 @@ class ValidateUsdModel(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["usdModel"]
- hosts = ["houdini"]
label = "Validate USD Model"
optional = True
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_output_node.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_output_node.py
similarity index 94%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_usd_output_node.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_output_node.py
index 968d64e8fc..88d549d46c 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_output_node.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_output_node.py
@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
import pyblish.api
+
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
-class ValidateUSDOutputNode(pyblish.api.InstancePlugin):
+
+class ValidateUSDOutputNode(plugin.HoudiniInstancePlugin):
"""Validate the instance USD LOPs Output Node.
This will ensure:
@@ -15,7 +18,6 @@ class ValidateUSDOutputNode(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["usd"]
- hosts = ["houdini"]
label = "Validate Output Node (USD)"
def process(self, instance):
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_render_product_names.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_render_product_names.py
similarity index 89%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_usd_render_product_names.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_render_product_names.py
index 4825b7cc71..eb46b266e2 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_render_product_names.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_render_product_names.py
@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
import os
-import pyblish.api
+import pyblish.api
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
-class ValidateUSDRenderProductNames(pyblish.api.InstancePlugin):
+
+class ValidateUSDRenderProductNames(plugin.HoudiniInstancePlugin):
"""Validate USD Render Product names are correctly set absolute paths."""
order = pyblish.api.ValidatorOrder
families = ["usdrender"]
- hosts = ["houdini"]
label = "Validate USD Render Product Names"
optional = True
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_setdress.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_setdress.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_usd_setdress.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_setdress.py
index 40b67e896a..3e91f0418f 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_setdress.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_setdress.py
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
import pyblish.api
-
-import ayon_core.hosts.houdini.api.usd as hou_usdlib
from ayon_core.pipeline import PublishValidationError
+from ayon_houdini.api import plugin
+import ayon_houdini.api.usd as hou_usdlib
-class ValidateUsdSetDress(pyblish.api.InstancePlugin):
+
+class ValidateUsdSetDress(plugin.HoudiniInstancePlugin):
"""Validate USD Set Dress.
Must only have references or payloads. May not generate new mesh or
@@ -15,14 +16,13 @@ class ValidateUsdSetDress(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["usdSetDress"]
- hosts = ["houdini"]
label = "Validate USD Set Dress"
optional = True
def process(self, instance):
- from pxr import UsdGeom
import hou
+ from pxr import UsdGeom
rop = hou.node(instance.data.get("instance_node"))
lop_path = hou_usdlib.get_usd_rop_loppath(rop)
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_shade_model_exists.py
similarity index 92%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_shade_model_exists.py
index 048d675c00..8a93d3b4a1 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_shade_model_exists.py
@@ -2,20 +2,19 @@
import re
import ayon_api
-import pyblish.api
-
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
KnownPublishError,
PublishValidationError,
)
+from ayon_houdini.api import plugin
-class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin):
+
+class ValidateUSDShadeModelExists(plugin.HoudiniInstancePlugin):
"""Validate the Instance has no current cooking errors."""
order = ValidateContentsOrder
- hosts = ["houdini"]
families = ["usdShade"]
label = "USD Shade model exists"
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_workspace.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_shade_workspace.py
similarity index 95%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_workspace.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_shade_workspace.py
index 2ea4b5d816..8972941253 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_usd_shade_workspace.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_usd_shade_workspace.py
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
+import hou
+
import pyblish.api
from ayon_core.pipeline import PublishValidationError
-import hou
+from ayon_houdini.api import plugin
-class ValidateUsdShadeWorkspace(pyblish.api.InstancePlugin):
+class ValidateUsdShadeWorkspace(plugin.HoudiniInstancePlugin):
"""Validate USD Shading Workspace is correct version.
There have been some issues with outdated/erroneous Shading Workspaces
@@ -14,7 +16,6 @@ class ValidateUsdShadeWorkspace(pyblish.api.InstancePlugin):
"""
order = pyblish.api.ValidatorOrder
- hosts = ["houdini"]
families = ["usdShade"]
label = "USD Shade Workspace"
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_vdb_output_node.py
similarity index 97%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_vdb_output_node.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_vdb_output_node.py
index 319a9a4b50..c4ed9d2fb8 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_vdb_output_node.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_vdb_output_node.py
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
import contextlib
-
-import pyblish.api
import hou
+import pyblish.api
from ayon_core.pipeline import PublishXmlValidationError
-from ayon_core.hosts.houdini.api.action import SelectInvalidAction
+
+from ayon_houdini.api import plugin
+from ayon_houdini.api.action import SelectInvalidAction
def group_consecutive_numbers(nums):
@@ -60,7 +61,7 @@ def get_geometry_at_frame(sop_node, frame, force=True):
return sop_node.geometryAtFrame(frame)
-class ValidateVDBOutputNode(pyblish.api.InstancePlugin):
+class ValidateVDBOutputNode(plugin.HoudiniInstancePlugin):
"""Validate that the node connected to the output node is of type VDB.
All primitives of the output geometry must be VDBs, no other primitive
@@ -81,7 +82,6 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder + 0.1
families = ["vdbcache"]
- hosts = ["houdini"]
label = "Validate Output Node (VDB)"
actions = [SelectInvalidAction]
diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_workfile_paths.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_workfile_paths.py
similarity index 96%
rename from client/ayon_core/hosts/houdini/plugins/publish/validate_workfile_paths.py
rename to server_addon/houdini/client/ayon_houdini/plugins/publish/validate_workfile_paths.py
index 7984b7615c..e1cd0c6caa 100644
--- a/client/ayon_core/hosts/houdini/plugins/publish/validate_workfile_paths.py
+++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_workfile_paths.py
@@ -1,20 +1,22 @@
# -*- coding: utf-8 -*-
-import pyblish.api
import hou
+
+import pyblish.api
from ayon_core.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
)
from ayon_core.pipeline.publish import RepairAction
+from ayon_houdini.api import plugin
+
class ValidateWorkfilePaths(
- pyblish.api.InstancePlugin, OptionalPyblishPluginMixin):
+ plugin.HoudiniInstancePlugin, OptionalPyblishPluginMixin):
"""Validate workfile paths so they are absolute."""
order = pyblish.api.ValidatorOrder
families = ["workfile"]
- hosts = ["houdini"]
label = "Validate Workfile Paths"
actions = [RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml b/server_addon/houdini/client/ayon_houdini/startup/MainMenuCommon.xml
similarity index 94%
rename from client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml
rename to server_addon/houdini/client/ayon_houdini/startup/MainMenuCommon.xml
index b6e78cbdc8..5b383f0085 100644
--- a/client/ayon_core/hosts/houdini/startup/MainMenuCommon.xml
+++ b/server_addon/houdini/client/ayon_houdini/startup/MainMenuCommon.xml
@@ -81,16 +81,16 @@ host_tools.show_workfiles(parent)
diff --git a/server_addon/houdini/client/ayon_houdini/startup/OPmenu.xml b/server_addon/houdini/client/ayon_houdini/startup/OPmenu.xml
new file mode 100644
index 0000000000..5637d2cf6a
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/startup/OPmenu.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
diff --git a/client/ayon_core/hosts/houdini/startup/python3.10libs/pythonrc.py b/server_addon/houdini/client/ayon_houdini/startup/python2.7libs/pythonrc.py
similarity index 78%
rename from client/ayon_core/hosts/houdini/startup/python3.10libs/pythonrc.py
rename to server_addon/houdini/client/ayon_houdini/startup/python2.7libs/pythonrc.py
index 6e45eb6a10..40ff7fb758 100644
--- a/client/ayon_core/hosts/houdini/startup/python3.10libs/pythonrc.py
+++ b/server_addon/houdini/client/ayon_houdini/startup/python2.7libs/pythonrc.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""OpenPype startup script."""
from ayon_core.pipeline import install_host
-from ayon_core.hosts.houdini.api import HoudiniHost
+from ayon_houdini.api import HoudiniHost
def main():
diff --git a/client/ayon_core/hosts/houdini/startup/python3.7libs/pythonrc.py b/server_addon/houdini/client/ayon_houdini/startup/python3.10libs/pythonrc.py
similarity index 78%
rename from client/ayon_core/hosts/houdini/startup/python3.7libs/pythonrc.py
rename to server_addon/houdini/client/ayon_houdini/startup/python3.10libs/pythonrc.py
index 6e45eb6a10..40ff7fb758 100644
--- a/client/ayon_core/hosts/houdini/startup/python3.7libs/pythonrc.py
+++ b/server_addon/houdini/client/ayon_houdini/startup/python3.10libs/pythonrc.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""OpenPype startup script."""
from ayon_core.pipeline import install_host
-from ayon_core.hosts.houdini.api import HoudiniHost
+from ayon_houdini.api import HoudiniHost
def main():
diff --git a/client/ayon_core/hosts/houdini/startup/python3.9libs/pythonrc.py b/server_addon/houdini/client/ayon_houdini/startup/python3.7libs/pythonrc.py
similarity index 78%
rename from client/ayon_core/hosts/houdini/startup/python3.9libs/pythonrc.py
rename to server_addon/houdini/client/ayon_houdini/startup/python3.7libs/pythonrc.py
index 6e45eb6a10..40ff7fb758 100644
--- a/client/ayon_core/hosts/houdini/startup/python3.9libs/pythonrc.py
+++ b/server_addon/houdini/client/ayon_houdini/startup/python3.7libs/pythonrc.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""OpenPype startup script."""
from ayon_core.pipeline import install_host
-from ayon_core.hosts.houdini.api import HoudiniHost
+from ayon_houdini.api import HoudiniHost
def main():
diff --git a/client/ayon_core/hosts/houdini/startup/python2.7libs/pythonrc.py b/server_addon/houdini/client/ayon_houdini/startup/python3.9libs/pythonrc.py
similarity index 78%
rename from client/ayon_core/hosts/houdini/startup/python2.7libs/pythonrc.py
rename to server_addon/houdini/client/ayon_houdini/startup/python3.9libs/pythonrc.py
index 6e45eb6a10..40ff7fb758 100644
--- a/client/ayon_core/hosts/houdini/startup/python2.7libs/pythonrc.py
+++ b/server_addon/houdini/client/ayon_houdini/startup/python3.9libs/pythonrc.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""OpenPype startup script."""
from ayon_core.pipeline import install_host
-from ayon_core.hosts.houdini.api import HoudiniHost
+from ayon_houdini.api import HoudiniHost
def main():
diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py
new file mode 100644
index 0000000000..a30c770e1d
--- /dev/null
+++ b/server_addon/houdini/client/ayon_houdini/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring Houdini addon version."""
+__version__ = "0.3.0"
diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py
index 4e441c76ae..275d21c1bf 100644
--- a/server_addon/houdini/package.py
+++ b/server_addon/houdini/package.py
@@ -1,3 +1,10 @@
name = "houdini"
title = "Houdini"
-version = "0.2.13"
+version = "0.3.0"
+
+client_dir = "ayon_houdini"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/server_addon/houdini/server/settings/create.py b/server_addon/houdini/server/settings/create.py
index 203ca4f9d6..cd1e110c23 100644
--- a/server_addon/houdini/server/settings/create.py
+++ b/server_addon/houdini/server/settings/create.py
@@ -57,6 +57,9 @@ class CreatePluginsModel(BaseSettingsModel):
CreateMantraROP: CreatorModel = SettingsField(
default_factory=CreatorModel,
title="Create Mantra ROP")
+ CreateModel: CreatorModel = SettingsField(
+ default_factory=CreatorModel,
+ title="Create Model")
CreatePointCache: CreatorModel = SettingsField(
default_factory=CreatorModel,
title="Create PointCache (Abc)")
@@ -124,6 +127,10 @@ DEFAULT_HOUDINI_CREATE_SETTINGS = {
"enabled": True,
"default_variants": ["Main"]
},
+ "CreateModel": {
+ "enabled": True,
+ "default_variants": ["Main"]
+ },
"CreatePointCache": {
"enabled": True,
"default_variants": ["Main"]
diff --git a/server_addon/houdini/server/settings/publish.py b/server_addon/houdini/server/settings/publish.py
index 8e0e7f7795..336de8e046 100644
--- a/server_addon/houdini/server/settings/publish.py
+++ b/server_addon/houdini/server/settings/publish.py
@@ -1,4 +1,7 @@
-from ayon_server.settings import BaseSettingsModel, SettingsField
+from ayon_server.settings import (
+ BaseSettingsModel,
+ SettingsField
+)
# Publish Plugins
@@ -20,6 +23,27 @@ class CollectChunkSizeModel(BaseSettingsModel):
title="Frames Per Task")
+class AOVFilterSubmodel(BaseSettingsModel):
+ """You should use the same host name you are using for Houdini."""
+ host_name: str = SettingsField("", title="Houdini Host name")
+ value: list[str] = SettingsField(
+ default_factory=list,
+ title="AOV regex"
+ )
+
+class CollectLocalRenderInstancesModel(BaseSettingsModel):
+
+ use_deadline_aov_filter: bool = SettingsField(
+ False,
+ title="Use Deadline AOV Filter"
+ )
+
+ aov_filter: AOVFilterSubmodel = SettingsField(
+ default_factory=AOVFilterSubmodel,
+ title="Reviewable products filter"
+ )
+
+
class ValidateWorkfilePathsModel(BaseSettingsModel):
enabled: bool = SettingsField(title="Enabled")
optional: bool = SettingsField(title="Optional")
@@ -49,13 +73,14 @@ class PublishPluginsModel(BaseSettingsModel):
default_factory=CollectChunkSizeModel,
title="Collect Chunk Size."
)
- ValidateContainers: BasicValidateModel = SettingsField(
- default_factory=BasicValidateModel,
- title="Validate Latest Containers.",
- section="Validators")
+ CollectLocalRenderInstances: CollectLocalRenderInstancesModel = SettingsField(
+ default_factory=CollectLocalRenderInstancesModel,
+ title="Collect Local Render Instances."
+ )
ValidateInstanceInContextHoudini: BasicValidateModel = SettingsField(
default_factory=BasicValidateModel,
- title="Validate Instance is in same Context.")
+ title="Validate Instance is in same Context.",
+ section="Validators")
ValidateMeshIsStatic: BasicValidateModel = SettingsField(
default_factory=BasicValidateModel,
title="Validate Mesh is Static.")
@@ -82,10 +107,14 @@ DEFAULT_HOUDINI_PUBLISH_SETTINGS = {
"optional": True,
"chunk_size": 999999
},
- "ValidateContainers": {
- "enabled": True,
- "optional": True,
- "active": True
+ "CollectLocalRenderInstances": {
+ "use_deadline_aov_filter": False,
+ "aov_filter" : {
+ "host_name": "houdini",
+ "value": [
+ ".*([Bb]eauty).*"
+ ]
+ }
},
"ValidateInstanceInContextHoudini": {
"enabled": True,
diff --git a/client/ayon_core/hosts/max/__init__.py b/server_addon/max/client/ayon_max/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/max/__init__.py
rename to server_addon/max/client/ayon_max/__init__.py
diff --git a/client/ayon_core/hosts/max/addon.py b/server_addon/max/client/ayon_max/addon.py
similarity index 100%
rename from client/ayon_core/hosts/max/addon.py
rename to server_addon/max/client/ayon_max/addon.py
diff --git a/client/ayon_core/hosts/max/api/__init__.py b/server_addon/max/client/ayon_max/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/max/api/__init__.py
rename to server_addon/max/client/ayon_max/api/__init__.py
diff --git a/client/ayon_core/hosts/max/api/action.py b/server_addon/max/client/ayon_max/api/action.py
similarity index 100%
rename from client/ayon_core/hosts/max/api/action.py
rename to server_addon/max/client/ayon_max/api/action.py
diff --git a/client/ayon_core/hosts/max/api/colorspace.py b/server_addon/max/client/ayon_max/api/colorspace.py
similarity index 100%
rename from client/ayon_core/hosts/max/api/colorspace.py
rename to server_addon/max/client/ayon_max/api/colorspace.py
diff --git a/client/ayon_core/hosts/max/api/lib.py b/server_addon/max/client/ayon_max/api/lib.py
similarity index 96%
rename from client/ayon_core/hosts/max/api/lib.py
rename to server_addon/max/client/ayon_max/api/lib.py
index d9a3af3336..eb22dbafd2 100644
--- a/client/ayon_core/hosts/max/api/lib.py
+++ b/server_addon/max/client/ayon_max/api/lib.py
@@ -6,12 +6,9 @@ import json
from typing import Any, Dict, Union
import six
-import ayon_api
from ayon_core.pipeline import (
get_current_project_name,
- get_current_folder_path,
- get_current_task_name,
colorspace
)
from ayon_core.settings import get_project_settings
@@ -23,7 +20,7 @@ from pymxs import runtime as rt
JSON_PREFIX = "JSON::"
-log = logging.getLogger("ayon_core.hosts.max")
+log = logging.getLogger("ayon_max")
def get_main_window():
@@ -372,12 +369,8 @@ def reset_colorspace():
"""
if int(get_max_version()) < 2024:
return
- project_name = get_current_project_name()
- colorspace_mgr = rt.ColorPipelineMgr
- project_settings = get_project_settings(project_name)
- max_config_data = colorspace.get_imageio_config(
- project_name, "max", project_settings)
+ max_config_data = colorspace.get_current_context_imageio_config_preset()
if max_config_data:
ocio_config_path = max_config_data["path"]
colorspace_mgr = rt.ColorPipelineMgr
@@ -392,10 +385,7 @@ def check_colorspace():
"because Max main window can't be found.")
if int(get_max_version()) >= 2024:
color_mgr = rt.ColorPipelineMgr
- project_name = get_current_project_name()
- project_settings = get_project_settings(project_name)
- max_config_data = colorspace.get_imageio_config(
- project_name, "max", project_settings)
+ max_config_data = colorspace.get_current_context_imageio_config_preset()
if max_config_data and color_mgr.Mode != rt.Name("OCIO_Custom"):
if not is_headless():
from ayon_core.tools.utils import SimplePopup
diff --git a/client/ayon_core/hosts/max/api/lib_renderproducts.py b/server_addon/max/client/ayon_max/api/lib_renderproducts.py
similarity index 99%
rename from client/ayon_core/hosts/max/api/lib_renderproducts.py
rename to server_addon/max/client/ayon_max/api/lib_renderproducts.py
index 710ed0031a..82a6a0c20c 100644
--- a/client/ayon_core/hosts/max/api/lib_renderproducts.py
+++ b/server_addon/max/client/ayon_max/api/lib_renderproducts.py
@@ -6,7 +6,7 @@ import os
from pymxs import runtime as rt
-from ayon_core.hosts.max.api.lib import get_current_renderer
+from ayon_max.api.lib import get_current_renderer
from ayon_core.pipeline import get_current_project_name
from ayon_core.settings import get_project_settings
diff --git a/client/ayon_core/hosts/max/api/lib_rendersettings.py b/server_addon/max/client/ayon_max/api/lib_rendersettings.py
similarity index 99%
rename from client/ayon_core/hosts/max/api/lib_rendersettings.py
rename to server_addon/max/client/ayon_max/api/lib_rendersettings.py
index 35b6d064c1..4b65e1397e 100644
--- a/client/ayon_core/hosts/max/api/lib_rendersettings.py
+++ b/server_addon/max/client/ayon_max/api/lib_rendersettings.py
@@ -5,7 +5,7 @@ from ayon_core.settings import get_project_settings
from ayon_core.pipeline import get_current_project_name
from ayon_core.pipeline.context_tools import get_current_folder_entity
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api.lib import (
set_render_frame_range,
get_current_renderer,
get_default_render_folder
diff --git a/client/ayon_core/hosts/max/api/menu.py b/server_addon/max/client/ayon_max/api/menu.py
similarity index 99%
rename from client/ayon_core/hosts/max/api/menu.py
rename to server_addon/max/client/ayon_max/api/menu.py
index c6ceeb3a43..25dd39fd84 100644
--- a/client/ayon_core/hosts/max/api/menu.py
+++ b/server_addon/max/client/ayon_max/api/menu.py
@@ -5,7 +5,7 @@ from qtpy import QtWidgets, QtCore
from pymxs import runtime as rt
from ayon_core.tools.utils import host_tools
-from ayon_core.hosts.max.api import lib
+from ayon_max.api import lib
class AYONMenu(object):
diff --git a/client/ayon_core/hosts/max/api/pipeline.py b/server_addon/max/client/ayon_max/api/pipeline.py
similarity index 94%
rename from client/ayon_core/hosts/max/api/pipeline.py
rename to server_addon/max/client/ayon_max/api/pipeline.py
index dc13f47795..5f5e896e86 100644
--- a/client/ayon_core/hosts/max/api/pipeline.py
+++ b/server_addon/max/client/ayon_max/api/pipeline.py
@@ -14,14 +14,14 @@ from ayon_core.pipeline import (
AVALON_CONTAINER_ID,
AYON_CONTAINER_ID,
)
-from ayon_core.hosts.max.api.menu import AYONMenu
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.plugin import MS_CUSTOM_ATTRIB
-from ayon_core.hosts.max import MAX_HOST_DIR
+from ayon_max.api.menu import AYONMenu
+from ayon_max.api import lib
+from ayon_max.api.plugin import MS_CUSTOM_ATTRIB
+from ayon_max import MAX_HOST_DIR
from pymxs import runtime as rt # noqa
-log = logging.getLogger("ayon_core.hosts.max")
+log = logging.getLogger("ayon_max")
PLUGINS_DIR = os.path.join(MAX_HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
@@ -52,11 +52,7 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
self._has_been_setup = True
- def context_setting():
- return lib.set_context_setting()
-
- rt.callbacks.addScript(rt.Name('systemPostNew'),
- context_setting)
+ rt.callbacks.addScript(rt.Name('systemPostNew'), on_new)
rt.callbacks.addScript(rt.Name('filePostOpen'),
lib.check_colorspace)
@@ -163,6 +159,14 @@ def ls() -> list:
yield lib.read(container)
+def on_new():
+ lib.set_context_setting()
+ if rt.checkForSave():
+ rt.resetMaxFile(rt.Name("noPrompt"))
+ rt.clearUndoBuffer()
+ rt.redrawViews()
+
+
def containerise(name: str, nodes: list, context,
namespace=None, loader=None, suffix="_CON"):
data = {
diff --git a/client/ayon_core/hosts/max/api/plugin.py b/server_addon/max/client/ayon_max/api/plugin.py
similarity index 100%
rename from client/ayon_core/hosts/max/api/plugin.py
rename to server_addon/max/client/ayon_max/api/plugin.py
diff --git a/client/ayon_core/hosts/max/api/preview_animation.py b/server_addon/max/client/ayon_max/api/preview_animation.py
similarity index 99%
rename from client/ayon_core/hosts/max/api/preview_animation.py
rename to server_addon/max/client/ayon_max/api/preview_animation.py
index 399d3b6222..acda5360a1 100644
--- a/client/ayon_core/hosts/max/api/preview_animation.py
+++ b/server_addon/max/client/ayon_max/api/preview_animation.py
@@ -3,7 +3,7 @@ import contextlib
from pymxs import runtime as rt
from .lib import get_max_version, render_resolution
-log = logging.getLogger("ayon_core.hosts.max")
+log = logging.getLogger("ayon_max")
@contextlib.contextmanager
diff --git a/client/ayon_core/hosts/max/hooks/force_startup_script.py b/server_addon/max/client/ayon_max/hooks/force_startup_script.py
similarity index 94%
rename from client/ayon_core/hosts/max/hooks/force_startup_script.py
rename to server_addon/max/client/ayon_max/hooks/force_startup_script.py
index 417f0049ab..1699ea300a 100644
--- a/client/ayon_core/hosts/max/hooks/force_startup_script.py
+++ b/server_addon/max/client/ayon_max/hooks/force_startup_script.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Pre-launch to force 3ds max startup script."""
import os
-from ayon_core.hosts.max import MAX_HOST_DIR
+from ayon_max import MAX_HOST_DIR
from ayon_applications import PreLaunchHook, LaunchTypes
diff --git a/client/ayon_core/hosts/max/hooks/inject_python.py b/server_addon/max/client/ayon_max/hooks/inject_python.py
similarity index 100%
rename from client/ayon_core/hosts/max/hooks/inject_python.py
rename to server_addon/max/client/ayon_max/hooks/inject_python.py
diff --git a/client/ayon_core/hosts/max/hooks/set_paths.py b/server_addon/max/client/ayon_max/hooks/set_paths.py
similarity index 100%
rename from client/ayon_core/hosts/max/hooks/set_paths.py
rename to server_addon/max/client/ayon_max/hooks/set_paths.py
diff --git a/client/ayon_core/hosts/fusion/vendor/urllib3/packages/backports/__init__.py b/server_addon/max/client/ayon_max/plugins/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/fusion/vendor/urllib3/packages/backports/__init__.py
rename to server_addon/max/client/ayon_max/plugins/__init__.py
diff --git a/client/ayon_core/hosts/max/plugins/create/create_camera.py b/server_addon/max/client/ayon_max/plugins/create/create_camera.py
similarity index 80%
rename from client/ayon_core/hosts/max/plugins/create/create_camera.py
rename to server_addon/max/client/ayon_max/plugins/create/create_camera.py
index 42f8cb716d..451e178afc 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_camera.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_camera.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating camera."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
class CreateCamera(plugin.MaxCreator):
@@ -9,3 +9,5 @@ class CreateCamera(plugin.MaxCreator):
label = "Camera"
product_type = "camera"
icon = "gear"
+
+ settings_category = "max"
diff --git a/client/ayon_core/hosts/max/plugins/create/create_maxScene.py b/server_addon/max/client/ayon_max/plugins/create/create_maxScene.py
similarity index 81%
rename from client/ayon_core/hosts/max/plugins/create/create_maxScene.py
rename to server_addon/max/client/ayon_max/plugins/create/create_maxScene.py
index 0e5768b267..ee58ef663d 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_maxScene.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_maxScene.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating raw max scene."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
class CreateMaxScene(plugin.MaxCreator):
@@ -9,3 +9,5 @@ class CreateMaxScene(plugin.MaxCreator):
label = "Max Scene"
product_type = "maxScene"
icon = "gear"
+
+ settings_category = "max"
diff --git a/client/ayon_core/hosts/max/plugins/create/create_model.py b/server_addon/max/client/ayon_max/plugins/create/create_model.py
similarity index 79%
rename from client/ayon_core/hosts/max/plugins/create/create_model.py
rename to server_addon/max/client/ayon_max/plugins/create/create_model.py
index 297c92067e..f48182ecd7 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_model.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_model.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for model."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
class CreateModel(plugin.MaxCreator):
@@ -9,3 +9,5 @@ class CreateModel(plugin.MaxCreator):
label = "Model"
product_type = "model"
icon = "gear"
+
+ settings_category = "max"
diff --git a/client/ayon_core/hosts/max/plugins/create/create_pointcache.py b/server_addon/max/client/ayon_max/plugins/create/create_pointcache.py
similarity index 82%
rename from client/ayon_core/hosts/max/plugins/create/create_pointcache.py
rename to server_addon/max/client/ayon_max/plugins/create/create_pointcache.py
index eb0686a0c0..6d7aabe12c 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_pointcache.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_pointcache.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating pointcache alembics."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
class CreatePointCache(plugin.MaxCreator):
@@ -9,3 +9,5 @@ class CreatePointCache(plugin.MaxCreator):
label = "Point Cache"
product_type = "pointcache"
icon = "gear"
+
+ settings_category = "max"
diff --git a/client/ayon_core/hosts/max/plugins/create/create_pointcloud.py b/server_addon/max/client/ayon_max/plugins/create/create_pointcloud.py
similarity index 82%
rename from client/ayon_core/hosts/max/plugins/create/create_pointcloud.py
rename to server_addon/max/client/ayon_max/plugins/create/create_pointcloud.py
index 9a58f4e624..52014d77b2 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_pointcloud.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_pointcloud.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating point cloud."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
class CreatePointCloud(plugin.MaxCreator):
@@ -9,3 +9,5 @@ class CreatePointCloud(plugin.MaxCreator):
label = "Point Cloud"
product_type = "pointcloud"
icon = "gear"
+
+ settings_category = "max"
diff --git a/client/ayon_core/hosts/max/plugins/create/create_redshift_proxy.py b/server_addon/max/client/ayon_max/plugins/create/create_redshift_proxy.py
similarity index 80%
rename from client/ayon_core/hosts/max/plugins/create/create_redshift_proxy.py
rename to server_addon/max/client/ayon_max/plugins/create/create_redshift_proxy.py
index 17f5349dc1..bcc96c7efe 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_redshift_proxy.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_redshift_proxy.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating camera."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
class CreateRedshiftProxy(plugin.MaxCreator):
@@ -8,3 +8,5 @@ class CreateRedshiftProxy(plugin.MaxCreator):
label = "Redshift Proxy"
product_type = "redshiftproxy"
icon = "gear"
+
+ settings_category = "max"
diff --git a/client/ayon_core/hosts/max/plugins/create/create_render.py b/server_addon/max/client/ayon_max/plugins/create/create_render.py
similarity index 93%
rename from client/ayon_core/hosts/max/plugins/create/create_render.py
rename to server_addon/max/client/ayon_max/plugins/create/create_render.py
index 60fe628a5e..d1e236f3ef 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_render.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_render.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating camera."""
import os
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
from ayon_core.lib import BoolDef
-from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings
+from ayon_max.api.lib_rendersettings import RenderSettings
class CreateRender(plugin.MaxCreator):
@@ -13,6 +13,8 @@ class CreateRender(plugin.MaxCreator):
product_type = "maxrender"
icon = "gear"
+ settings_category = "max"
+
def create(self, product_name, instance_data, pre_create_data):
from pymxs import runtime as rt
file = rt.maxFileName
diff --git a/client/ayon_core/hosts/max/plugins/create/create_review.py b/server_addon/max/client/ayon_max/plugins/create/create_review.py
similarity index 98%
rename from client/ayon_core/hosts/max/plugins/create/create_review.py
rename to server_addon/max/client/ayon_max/plugins/create/create_review.py
index 0a0ffd2e46..a49490519a 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_review.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_review.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating review in Max."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
from ayon_core.lib import BoolDef, EnumDef, NumberDef
@@ -12,6 +12,8 @@ class CreateReview(plugin.MaxCreator):
product_type = "review"
icon = "video-camera"
+ settings_category = "max"
+
review_width = 1920
review_height = 1080
percentSize = 100
diff --git a/client/ayon_core/hosts/max/plugins/create/create_tycache.py b/server_addon/max/client/ayon_max/plugins/create/create_tycache.py
similarity index 80%
rename from client/ayon_core/hosts/max/plugins/create/create_tycache.py
rename to server_addon/max/client/ayon_max/plugins/create/create_tycache.py
index 2b3893bf13..cbdd94e272 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_tycache.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_tycache.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating TyCache."""
-from ayon_core.hosts.max.api import plugin
+from ayon_max.api import plugin
class CreateTyCache(plugin.MaxCreator):
@@ -9,3 +9,5 @@ class CreateTyCache(plugin.MaxCreator):
label = "TyCache"
product_type = "tycache"
icon = "gear"
+
+ settings_category = "max"
diff --git a/client/ayon_core/hosts/max/plugins/create/create_workfile.py b/server_addon/max/client/ayon_max/plugins/create/create_workfile.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/create/create_workfile.py
rename to server_addon/max/client/ayon_max/plugins/create/create_workfile.py
index 901da6254c..35c41f0fcc 100644
--- a/client/ayon_core/hosts/max/plugins/create/create_workfile.py
+++ b/server_addon/max/client/ayon_max/plugins/create/create_workfile.py
@@ -3,8 +3,8 @@
import ayon_api
from ayon_core.pipeline import CreatedInstance, AutoCreator
-from ayon_core.hosts.max.api import plugin
-from ayon_core.hosts.max.api.lib import read, imprint
+from ayon_max.api import plugin
+from ayon_max.api.lib import read, imprint
from pymxs import runtime as rt
@@ -17,6 +17,8 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator):
default_variant = "Main"
+ settings_category = "max"
+
def create(self):
variant = self.default_variant
current_instance = next(
diff --git a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py b/server_addon/max/client/ayon_max/plugins/load/load_camera_fbx.py
similarity index 96%
rename from client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py
rename to server_addon/max/client/ayon_max/plugins/load/load_camera_fbx.py
index 6f1e9988c5..81ea15d52a 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_camera_fbx.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_camera_fbx.py
@@ -1,12 +1,12 @@
import os
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib
+from ayon_max.api.lib import (
unique_namespace,
get_namespace,
object_transform_set
)
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
update_custom_attribute_data,
diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/server_addon/max/client/ayon_max/plugins/load/load_max_scene.py
similarity index 98%
rename from client/ayon_core/hosts/max/plugins/load/load_max_scene.py
rename to server_addon/max/client/ayon_max/plugins/load/load_max_scene.py
index 97b8c6cd52..7fca69b193 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_max_scene.py
@@ -1,14 +1,14 @@
import os
from qtpy import QtWidgets, QtCore
from ayon_core.lib.attribute_definitions import EnumDef
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib
+from ayon_max.api.lib import (
unique_namespace,
get_namespace,
object_transform_set,
is_headless
)
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise, get_previous_loaded_object,
update_custom_attribute_data,
remove_container_data
diff --git a/client/ayon_core/hosts/max/plugins/load/load_model.py b/server_addon/max/client/ayon_max/plugins/load/load_model.py
similarity index 96%
rename from client/ayon_core/hosts/max/plugins/load/load_model.py
rename to server_addon/max/client/ayon_max/plugins/load/load_model.py
index 1070fce2bd..2a6bc45c18 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_model.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_model.py
@@ -1,12 +1,12 @@
import os
from ayon_core.pipeline import load, get_representation_path
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
remove_container_data
)
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib
+from ayon_max.api.lib import (
maintained_selection, unique_namespace
)
diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py b/server_addon/max/client/ayon_max/plugins/load/load_model_fbx.py
similarity index 94%
rename from client/ayon_core/hosts/max/plugins/load/load_model_fbx.py
rename to server_addon/max/client/ayon_max/plugins/load/load_model_fbx.py
index 6f5de20ae0..2775e1b453 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_model_fbx.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_model_fbx.py
@@ -1,17 +1,17 @@
import os
from ayon_core.pipeline import load, get_representation_path
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise, get_previous_loaded_object,
update_custom_attribute_data,
remove_container_data
)
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib
+from ayon_max.api.lib import (
unique_namespace,
get_namespace,
object_transform_set
)
-from ayon_core.hosts.max.api.lib import maintained_selection
+from ayon_max.api.lib import maintained_selection
class FbxModelLoader(load.LoaderPlugin):
diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py b/server_addon/max/client/ayon_max/plugins/load/load_model_obj.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/load/load_model_obj.py
rename to server_addon/max/client/ayon_max/plugins/load/load_model_obj.py
index a9119259df..d38aadb5bc 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_model_obj.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_model_obj.py
@@ -1,13 +1,13 @@
import os
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib
+from ayon_max.api.lib import (
unique_namespace,
get_namespace,
maintained_selection,
object_transform_set
)
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
update_custom_attribute_data,
diff --git a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py b/server_addon/max/client/ayon_max/plugins/load/load_model_usd.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/load/load_model_usd.py
rename to server_addon/max/client/ayon_max/plugins/load/load_model_usd.py
index 2ed5d64a18..f4dd41d5db 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_model_usd.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_model_usd.py
@@ -2,15 +2,15 @@ import os
from pymxs import runtime as rt
from ayon_core.pipeline.load import LoadError
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib
+from ayon_max.api.lib import (
unique_namespace,
get_namespace,
object_transform_set,
get_plugins
)
-from ayon_core.hosts.max.api.lib import maintained_selection
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.lib import maintained_selection
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
update_custom_attribute_data,
diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py b/server_addon/max/client/ayon_max/plugins/load/load_pointcache.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/load/load_pointcache.py
rename to server_addon/max/client/ayon_max/plugins/load/load_pointcache.py
index 0743b3bb34..d7def3d0ba 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_pointcache.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_pointcache.py
@@ -6,9 +6,9 @@ Because of limited api, alembics can be only loaded, but not easily updated.
"""
import os
from ayon_core.pipeline import load, get_representation_path
-from ayon_core.hosts.max.api import lib, maintained_selection
-from ayon_core.hosts.max.api.lib import unique_namespace
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api import lib, maintained_selection
+from ayon_max.api.lib import unique_namespace
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
remove_container_data
diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py b/server_addon/max/client/ayon_max/plugins/load/load_pointcache_ornatrix.py
similarity index 96%
rename from client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py
rename to server_addon/max/client/ayon_max/plugins/load/load_pointcache_ornatrix.py
index 47690f84e9..bc997951c1 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_pointcache_ornatrix.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_pointcache_ornatrix.py
@@ -1,20 +1,20 @@
import os
from ayon_core.pipeline import load, get_representation_path
from ayon_core.pipeline.load import LoadError
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
update_custom_attribute_data,
remove_container_data
)
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api.lib import (
unique_namespace,
get_namespace,
object_transform_set,
get_plugins
)
-from ayon_core.hosts.max.api import lib
+from ayon_max.api import lib
from pymxs import runtime as rt
diff --git a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py b/server_addon/max/client/ayon_max/plugins/load/load_pointcloud.py
similarity index 92%
rename from client/ayon_core/hosts/max/plugins/load/load_pointcloud.py
rename to server_addon/max/client/ayon_max/plugins/load/load_pointcloud.py
index 0e79882fc5..0fb506d5bd 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_pointcloud.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_pointcloud.py
@@ -1,11 +1,11 @@
import os
-from ayon_core.hosts.max.api import lib, maintained_selection
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib, maintained_selection
+from ayon_max.api.lib import (
unique_namespace,
)
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
update_custom_attribute_data,
diff --git a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py b/server_addon/max/client/ayon_max/plugins/load/load_redshift_proxy.py
similarity index 94%
rename from client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py
rename to server_addon/max/client/ayon_max/plugins/load/load_redshift_proxy.py
index 22d42390d9..3fd84b7538 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_redshift_proxy.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_redshift_proxy.py
@@ -6,14 +6,14 @@ from ayon_core.pipeline import (
get_representation_path
)
from ayon_core.pipeline.load import LoadError
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise,
update_custom_attribute_data,
get_previous_loaded_object,
remove_container_data
)
-from ayon_core.hosts.max.api import lib
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib
+from ayon_max.api.lib import (
unique_namespace,
get_plugins
)
diff --git a/client/ayon_core/hosts/max/plugins/load/load_tycache.py b/server_addon/max/client/ayon_max/plugins/load/load_tycache.py
similarity index 92%
rename from client/ayon_core/hosts/max/plugins/load/load_tycache.py
rename to server_addon/max/client/ayon_max/plugins/load/load_tycache.py
index 7a5296d933..e087d5599a 100644
--- a/client/ayon_core/hosts/max/plugins/load/load_tycache.py
+++ b/server_addon/max/client/ayon_max/plugins/load/load_tycache.py
@@ -1,10 +1,10 @@
import os
-from ayon_core.hosts.max.api import lib, maintained_selection
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api import lib, maintained_selection
+from ayon_max.api.lib import (
unique_namespace,
)
-from ayon_core.hosts.max.api.pipeline import (
+from ayon_max.api.pipeline import (
containerise,
get_previous_loaded_object,
update_custom_attribute_data,
diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_current_file.py b/server_addon/max/client/ayon_max/plugins/publish/collect_current_file.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/collect_current_file.py
rename to server_addon/max/client/ayon_max/plugins/publish/collect_current_file.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_frame_range.py b/server_addon/max/client/ayon_max/plugins/publish/collect_frame_range.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/collect_frame_range.py
rename to server_addon/max/client/ayon_max/plugins/publish/collect_frame_range.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_members.py b/server_addon/max/client/ayon_max/plugins/publish/collect_members.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/collect_members.py
rename to server_addon/max/client/ayon_max/plugins/publish/collect_members.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_render.py b/server_addon/max/client/ayon_max/plugins/publish/collect_render.py
similarity index 94%
rename from client/ayon_core/hosts/max/plugins/publish/collect_render.py
rename to server_addon/max/client/ayon_max/plugins/publish/collect_render.py
index 4ad9dfb3a3..a5e8d65df2 100644
--- a/client/ayon_core/hosts/max/plugins/publish/collect_render.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/collect_render.py
@@ -5,10 +5,10 @@ import pyblish.api
from pymxs import runtime as rt
from ayon_core.pipeline.publish import KnownPublishError
-from ayon_core.hosts.max.api import colorspace
-from ayon_core.hosts.max.api.lib import get_max_version, get_current_renderer
-from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings
-from ayon_core.hosts.max.api.lib_renderproducts import RenderProducts
+from ayon_max.api import colorspace
+from ayon_max.api.lib import get_max_version, get_current_renderer
+from ayon_max.api.lib_rendersettings import RenderSettings
+from ayon_max.api.lib_renderproducts import RenderProducts
class CollectRender(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_review.py b/server_addon/max/client/ayon_max/plugins/publish/collect_review.py
similarity index 99%
rename from client/ayon_core/hosts/max/plugins/publish/collect_review.py
rename to server_addon/max/client/ayon_max/plugins/publish/collect_review.py
index d746e2b2db..321aa7439c 100644
--- a/client/ayon_core/hosts/max/plugins/publish/collect_review.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/collect_review.py
@@ -4,7 +4,7 @@ import pyblish.api
from pymxs import runtime as rt
from ayon_core.lib import BoolDef
-from ayon_core.hosts.max.api.lib import get_max_version
+from ayon_max.api.lib import get_max_version
from ayon_core.pipeline.publish import (
AYONPyblishPluginMixin,
KnownPublishError
diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_tycache_attributes.py b/server_addon/max/client/ayon_max/plugins/publish/collect_tycache_attributes.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/collect_tycache_attributes.py
rename to server_addon/max/client/ayon_max/plugins/publish/collect_tycache_attributes.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/collect_workfile.py b/server_addon/max/client/ayon_max/plugins/publish/collect_workfile.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/collect_workfile.py
rename to server_addon/max/client/ayon_max/plugins/publish/collect_workfile.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_alembic.py b/server_addon/max/client/ayon_max/plugins/publish/extract_alembic.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/extract_alembic.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_alembic.py
index 67cec23ecc..b0999e5a78 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_alembic.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_alembic.py
@@ -41,8 +41,8 @@ import os
import pyblish.api
from ayon_core.pipeline import publish, OptionalPyblishPluginMixin
from pymxs import runtime as rt
-from ayon_core.hosts.max.api import maintained_selection
-from ayon_core.hosts.max.api.lib import suspended_refresh
+from ayon_max.api import maintained_selection
+from ayon_max.api.lib import suspended_refresh
from ayon_core.lib import BoolDef
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_fbx.py b/server_addon/max/client/ayon_max/plugins/publish/extract_fbx.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/publish/extract_fbx.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_fbx.py
index 3d80588c47..bdfc1d0d78 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_fbx.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_fbx.py
@@ -2,8 +2,8 @@ import os
import pyblish.api
from ayon_core.pipeline import publish, OptionalPyblishPluginMixin
from pymxs import runtime as rt
-from ayon_core.hosts.max.api import maintained_selection
-from ayon_core.hosts.max.api.lib import convert_unit_scale
+from ayon_max.api import maintained_selection
+from ayon_max.api.lib import convert_unit_scale
class ExtractModelFbx(publish.Extractor, OptionalPyblishPluginMixin):
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_max_scene_raw.py b/server_addon/max/client/ayon_max/plugins/publish/extract_max_scene_raw.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/extract_max_scene_raw.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_max_scene_raw.py
index f5c703564c..ecde6d2ce9 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_max_scene_raw.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_max_scene_raw.py
@@ -15,6 +15,8 @@ class ExtractMaxSceneRaw(publish.Extractor, OptionalPyblishPluginMixin):
families = ["camera", "maxScene", "model"]
optional = True
+ settings_category = "max"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_model_obj.py b/server_addon/max/client/ayon_max/plugins/publish/extract_model_obj.py
similarity index 93%
rename from client/ayon_core/hosts/max/plugins/publish/extract_model_obj.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_model_obj.py
index 03bdde7d5d..6556bd7809 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_model_obj.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_model_obj.py
@@ -2,8 +2,8 @@ import os
import pyblish.api
from ayon_core.pipeline import publish, OptionalPyblishPluginMixin
from pymxs import runtime as rt
-from ayon_core.hosts.max.api import maintained_selection
-from ayon_core.hosts.max.api.lib import suspended_refresh
+from ayon_max.api import maintained_selection
+from ayon_max.api.lib import suspended_refresh
from ayon_core.pipeline.publish import KnownPublishError
@@ -18,6 +18,8 @@ class ExtractModelObj(publish.Extractor, OptionalPyblishPluginMixin):
families = ["model"]
optional = True
+ settings_category = "max"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_model_usd.py b/server_addon/max/client/ayon_max/plugins/publish/extract_model_usd.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/extract_model_usd.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_model_usd.py
index 64791e4c7d..a48126c6e5 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_model_usd.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_model_usd.py
@@ -3,7 +3,7 @@ import os
import pyblish.api
from pymxs import runtime as rt
-from ayon_core.hosts.max.api import maintained_selection
+from ayon_max.api import maintained_selection
from ayon_core.pipeline import OptionalPyblishPluginMixin, publish
@@ -17,6 +17,8 @@ class ExtractModelUSD(publish.Extractor,
families = ["model"]
optional = True
+ settings_category = "max"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_pointcloud.py b/server_addon/max/client/ayon_max/plugins/publish/extract_pointcloud.py
similarity index 99%
rename from client/ayon_core/hosts/max/plugins/publish/extract_pointcloud.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_pointcloud.py
index 67dde7f0a6..f763325eb9 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_pointcloud.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_pointcloud.py
@@ -3,7 +3,7 @@ import os
import pyblish.api
from pymxs import runtime as rt
-from ayon_core.hosts.max.api import maintained_selection
+from ayon_max.api import maintained_selection
from ayon_core.pipeline import publish
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_redshift_proxy.py b/server_addon/max/client/ayon_max/plugins/publish/extract_redshift_proxy.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/extract_redshift_proxy.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_redshift_proxy.py
index 6a647670bc..dfb3527be1 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_redshift_proxy.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_redshift_proxy.py
@@ -2,7 +2,7 @@ import os
import pyblish.api
from ayon_core.pipeline import publish
from pymxs import runtime as rt
-from ayon_core.hosts.max.api import maintained_selection
+from ayon_max.api import maintained_selection
class ExtractRedshiftProxy(publish.Extractor):
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_review_animation.py b/server_addon/max/client/ayon_max/plugins/publish/extract_review_animation.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/extract_review_animation.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_review_animation.py
index 12f1fbb63b..b6397d404e 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_review_animation.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_review_animation.py
@@ -1,7 +1,7 @@
import os
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.max.api.preview_animation import (
+from ayon_max.api.preview_animation import (
render_preview_animation
)
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_thumbnail.py b/server_addon/max/client/ayon_max/plugins/publish/extract_thumbnail.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/publish/extract_thumbnail.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_thumbnail.py
index 5764ce98c4..183e381be2 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_thumbnail.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_thumbnail.py
@@ -1,7 +1,7 @@
import os
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.max.api.preview_animation import render_preview_animation
+from ayon_max.api.preview_animation import render_preview_animation
class ExtractThumbnail(publish.Extractor):
diff --git a/client/ayon_core/hosts/max/plugins/publish/extract_tycache.py b/server_addon/max/client/ayon_max/plugins/publish/extract_tycache.py
similarity index 98%
rename from client/ayon_core/hosts/max/plugins/publish/extract_tycache.py
rename to server_addon/max/client/ayon_max/plugins/publish/extract_tycache.py
index 50bb06a765..576abe32a2 100644
--- a/client/ayon_core/hosts/max/plugins/publish/extract_tycache.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/extract_tycache.py
@@ -3,7 +3,7 @@ import os
import pyblish.api
from pymxs import runtime as rt
-from ayon_core.hosts.max.api import maintained_selection
+from ayon_max.api import maintained_selection
from ayon_core.pipeline import publish
diff --git a/client/ayon_core/hosts/max/plugins/publish/help/validate_model_name.xml b/server_addon/max/client/ayon_max/plugins/publish/help/validate_model_name.xml
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/help/validate_model_name.xml
rename to server_addon/max/client/ayon_max/plugins/publish/help/validate_model_name.xml
diff --git a/client/ayon_core/hosts/max/plugins/publish/increment_workfile_version.py b/server_addon/max/client/ayon_max/plugins/publish/increment_workfile_version.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/increment_workfile_version.py
rename to server_addon/max/client/ayon_max/plugins/publish/increment_workfile_version.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/save_scene.py b/server_addon/max/client/ayon_max/plugins/publish/save_scene.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/save_scene.py
rename to server_addon/max/client/ayon_max/plugins/publish/save_scene.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/save_scenes_for_cameras.py b/server_addon/max/client/ayon_max/plugins/publish/save_scenes_for_cameras.py
similarity index 96%
rename from client/ayon_core/hosts/max/plugins/publish/save_scenes_for_cameras.py
rename to server_addon/max/client/ayon_max/plugins/publish/save_scenes_for_cameras.py
index 817db1b28f..a211210550 100644
--- a/client/ayon_core/hosts/max/plugins/publish/save_scenes_for_cameras.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/save_scenes_for_cameras.py
@@ -5,8 +5,8 @@ import tempfile
from pymxs import runtime as rt
from ayon_core.lib import run_subprocess
-from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings
-from ayon_core.hosts.max.api.lib_renderproducts import RenderProducts
+from ayon_max.api.lib_rendersettings import RenderSettings
+from ayon_max.api.lib_renderproducts import RenderProducts
class SaveScenesForCamera(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_attributes.py b/server_addon/max/client/ayon_max/plugins/publish/validate_attributes.py
similarity index 99%
rename from client/ayon_core/hosts/max/plugins/publish/validate_attributes.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_attributes.py
index 354539871f..a489533b2c 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_attributes.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_attributes.py
@@ -61,6 +61,8 @@ class ValidateAttributes(OptionalPyblishPluginMixin,
actions = [RepairContextAction]
optional = True
+ settings_category = "max"
+
@classmethod
def get_invalid(cls, context):
attributes = json.loads(
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_camera_attributes.py b/server_addon/max/client/ayon_max/plugins/publish/validate_camera_attributes.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/validate_camera_attributes.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_camera_attributes.py
index 9398cba2b7..63a2ef39a7 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_camera_attributes.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_camera_attributes.py
@@ -6,7 +6,7 @@ from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin,
PublishValidationError
)
-from ayon_core.hosts.max.api.action import SelectInvalidAction
+from ayon_max.api.action import SelectInvalidAction
class ValidateCameraAttributes(OptionalPyblishPluginMixin,
@@ -23,6 +23,8 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin,
actions = [SelectInvalidAction, RepairAction]
optional = True
+ settings_category = "max"
+
DEFAULTS = ["fov", "nearrange", "farrange",
"nearclip", "farclip"]
CAM_TYPE = ["Freecamera", "Targetcamera",
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_camera_contents.py b/server_addon/max/client/ayon_max/plugins/publish/validate_camera_contents.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/validate_camera_contents.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_camera_contents.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py b/server_addon/max/client/ayon_max/plugins/publish/validate_extended_viewport.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/validate_extended_viewport.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_extended_viewport.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py b/server_addon/max/client/ayon_max/plugins/publish/validate_frame_range.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_frame_range.py
index 11b55232d5..9a9f22dd3e 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_frame_range.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_frame_range.py
@@ -10,7 +10,7 @@ from ayon_core.pipeline.publish import (
PublishValidationError,
KnownPublishError
)
-from ayon_core.hosts.max.api.lib import get_frame_range, set_timeline
+from ayon_max.api.lib import get_frame_range, set_timeline
class ValidateFrameRange(pyblish.api.InstancePlugin,
@@ -36,6 +36,8 @@ class ValidateFrameRange(pyblish.api.InstancePlugin,
optional = True
actions = [RepairAction]
+ settings_category = "max"
+
def process(self, instance):
if not self.is_active(instance.data):
self.log.debug("Skipping Validate Frame Range...")
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_has_members.py b/server_addon/max/client/ayon_max/plugins/publish/validate_instance_has_members.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/validate_instance_has_members.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_instance_has_members.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py b/server_addon/max/client/ayon_max/plugins/publish/validate_instance_in_context.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_instance_in_context.py
index 5107665235..d5bdfe4eb0 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_instance_in_context.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_instance_in_context.py
@@ -7,7 +7,7 @@ from ayon_core.pipeline.publish import (
PublishValidationError,
OptionalPyblishPluginMixin
)
-from ayon_core.hosts.max.api.action import SelectInvalidAction
+from ayon_max.api.action import SelectInvalidAction
from pymxs import runtime as rt
@@ -27,6 +27,8 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin,
hosts = ["max"]
actions = [SelectInvalidAction, RepairAction]
+ settings_category = "max"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_loaded_plugin.py b/server_addon/max/client/ayon_max/plugins/publish/validate_loaded_plugin.py
similarity index 98%
rename from client/ayon_core/hosts/max/plugins/publish/validate_loaded_plugin.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_loaded_plugin.py
index e278041b6b..1fddc7998d 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_loaded_plugin.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_loaded_plugin.py
@@ -9,7 +9,7 @@ from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin,
PublishValidationError
)
-from ayon_core.hosts.max.api.lib import get_plugins
+from ayon_max.api.lib import get_plugins
class ValidateLoadedPlugin(OptionalPyblishPluginMixin,
@@ -25,6 +25,8 @@ class ValidateLoadedPlugin(OptionalPyblishPluginMixin,
optional = True
actions = [RepairAction]
+ settings_category = "max"
+
family_plugins_mapping = []
@classmethod
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py b/server_addon/max/client/ayon_max/plugins/publish/validate_mesh_has_uv.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_mesh_has_uv.py
index ccd91da2be..31143a60c0 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_mesh_has_uv.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_mesh_has_uv.py
@@ -1,6 +1,6 @@
import pyblish.api
-from ayon_core.hosts.max.api.action import SelectInvalidAction
+from ayon_max.api.action import SelectInvalidAction
from ayon_core.pipeline.publish import (
ValidateMeshOrder,
OptionalPyblishPluginMixin,
@@ -30,6 +30,8 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin,
actions = [SelectInvalidAction]
optional = True
+ settings_category = "max"
+
@classmethod
def get_invalid(cls, instance):
meshes = [member for member in instance.data["members"]
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_contents.py b/server_addon/max/client/ayon_max/plugins/publish/validate_model_contents.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/validate_model_contents.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_model_contents.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py b/server_addon/max/client/ayon_max/plugins/publish/validate_model_name.py
similarity index 97%
rename from client/ayon_core/hosts/max/plugins/publish/validate_model_name.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_model_name.py
index eb86e2e5bd..d691b739b7 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_model_name.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_model_name.py
@@ -4,7 +4,7 @@ import re
import pyblish.api
-from ayon_core.hosts.max.api.action import SelectInvalidAction
+from ayon_max.api.action import SelectInvalidAction
from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin,
@@ -39,6 +39,9 @@ class ValidateModelName(pyblish.api.InstancePlugin,
families = ["model"]
label = "Validate Model Name"
actions = [SelectInvalidAction]
+
+ settings_category = "max"
+
# defined by settings
regex = r"(.*)_(?P.*)_(GEO)"
# cache
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py b/server_addon/max/client/ayon_max/plugins/publish/validate_no_animation.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_no_animation.py
index 4b2a18d606..26384954ca 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_no_animation.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_no_animation.py
@@ -5,7 +5,7 @@ from ayon_core.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
)
-from ayon_core.hosts.max.api.action import SelectInvalidAction
+from ayon_max.api.action import SelectInvalidAction
def get_invalid_keys(obj):
@@ -39,6 +39,8 @@ class ValidateNoAnimation(pyblish.api.InstancePlugin,
label = "Validate No Animation"
actions = [SelectInvalidAction]
+ settings_category = "max"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_pointcloud.py b/server_addon/max/client/ayon_max/plugins/publish/validate_pointcloud.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/validate_pointcloud.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_pointcloud.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderable_camera.py b/server_addon/max/client/ayon_max/plugins/publish/validate_renderable_camera.py
similarity index 95%
rename from client/ayon_core/hosts/max/plugins/publish/validate_renderable_camera.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_renderable_camera.py
index ffd6b183fe..dc05771e1b 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_renderable_camera.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_renderable_camera.py
@@ -4,7 +4,7 @@ from ayon_core.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin)
from ayon_core.pipeline.publish import RepairAction
-from ayon_core.hosts.max.api.lib import get_current_renderer
+from ayon_max.api.lib import get_current_renderer
from pymxs import runtime as rt
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderer_redshift_proxy.py b/server_addon/max/client/ayon_max/plugins/publish/validate_renderer_redshift_proxy.py
similarity index 96%
rename from client/ayon_core/hosts/max/plugins/publish/validate_renderer_redshift_proxy.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_renderer_redshift_proxy.py
index de3a806c85..66c69bc100 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_renderer_redshift_proxy.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_renderer_redshift_proxy.py
@@ -3,7 +3,7 @@ import pyblish.api
from ayon_core.pipeline import PublishValidationError
from pymxs import runtime as rt
from ayon_core.pipeline.publish import RepairAction
-from ayon_core.hosts.max.api.lib import get_current_renderer
+from ayon_max.api.lib import get_current_renderer
class ValidateRendererRedshiftProxy(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py b/server_addon/max/client/ayon_max/plugins/publish/validate_renderpasses.py
similarity index 98%
rename from client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_renderpasses.py
index 394d3119c4..d0d47c6340 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_renderpasses.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_renderpasses.py
@@ -7,7 +7,7 @@ from ayon_core.pipeline.publish import (
PublishValidationError,
OptionalPyblishPluginMixin
)
-from ayon_core.hosts.max.api.lib_rendersettings import RenderSettings
+from ayon_max.api.lib_rendersettings import RenderSettings
class ValidateRenderPasses(OptionalPyblishPluginMixin,
@@ -21,6 +21,8 @@ class ValidateRenderPasses(OptionalPyblishPluginMixin,
label = "Validate Render Passes"
actions = [RepairAction]
+ settings_category = "max"
+
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py b/server_addon/max/client/ayon_max/plugins/publish/validate_resolution_setting.py
similarity index 98%
rename from client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_resolution_setting.py
index 5f6cd0a21d..9f7ec17dd9 100644
--- a/client/ayon_core/hosts/max/plugins/publish/validate_resolution_setting.py
+++ b/server_addon/max/client/ayon_max/plugins/publish/validate_resolution_setting.py
@@ -7,7 +7,7 @@ from ayon_core.pipeline.publish import (
RepairAction,
PublishValidationError
)
-from ayon_core.hosts.max.api.lib import (
+from ayon_max.api.lib import (
reset_scene_resolution,
imprint
)
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_scene_saved.py b/server_addon/max/client/ayon_max/plugins/publish/validate_scene_saved.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/validate_scene_saved.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_scene_saved.py
diff --git a/client/ayon_core/hosts/max/plugins/publish/validate_tyflow_data.py b/server_addon/max/client/ayon_max/plugins/publish/validate_tyflow_data.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/publish/validate_tyflow_data.py
rename to server_addon/max/client/ayon_max/plugins/publish/validate_tyflow_data.py
diff --git a/client/ayon_core/hosts/max/startup/startup.ms b/server_addon/max/client/ayon_max/startup/startup.ms
similarity index 100%
rename from client/ayon_core/hosts/max/startup/startup.ms
rename to server_addon/max/client/ayon_max/startup/startup.ms
diff --git a/client/ayon_core/hosts/max/startup/startup.py b/server_addon/max/client/ayon_max/startup/startup.py
similarity index 88%
rename from client/ayon_core/hosts/max/startup/startup.py
rename to server_addon/max/client/ayon_max/startup/startup.py
index 49a861bad0..1462cc93b7 100644
--- a/client/ayon_core/hosts/max/startup/startup.py
+++ b/server_addon/max/client/ayon_max/startup/startup.py
@@ -1,15 +1,13 @@
# -*- coding: utf-8 -*-
import os
import sys
-
+from ayon_max.api import MaxHost
+from ayon_core.pipeline import install_host
# this might happen in some 3dsmax version where PYTHONPATH isn't added
# to sys.path automatically
for path in os.environ["PYTHONPATH"].split(os.pathsep):
if path and path not in sys.path:
sys.path.append(path)
-from ayon_core.hosts.max.api import MaxHost
-from ayon_core.pipeline import install_host
-
host = MaxHost()
install_host(host)
diff --git a/server_addon/max/package.py b/server_addon/max/package.py
index fb1f1b3050..ddd4e3b33e 100644
--- a/server_addon/max/package.py
+++ b/server_addon/max/package.py
@@ -1,3 +1,9 @@
name = "max"
title = "Max"
-version = "0.1.7"
+version = "0.2.0"
+client_dir = "ayon_max"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/client/ayon_core/hosts/maya/__init__.py b/server_addon/maya/client/ayon_maya/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/maya/__init__.py
rename to server_addon/maya/client/ayon_maya/__init__.py
diff --git a/client/ayon_core/hosts/maya/addon.py b/server_addon/maya/client/ayon_maya/addon.py
similarity index 100%
rename from client/ayon_core/hosts/maya/addon.py
rename to server_addon/maya/client/ayon_maya/addon.py
diff --git a/client/ayon_core/hosts/maya/api/__init__.py b/server_addon/maya/client/ayon_maya/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/__init__.py
rename to server_addon/maya/client/ayon_maya/api/__init__.py
diff --git a/client/ayon_core/hosts/maya/api/action.py b/server_addon/maya/client/ayon_maya/api/action.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/action.py
rename to server_addon/maya/client/ayon_maya/api/action.py
diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/server_addon/maya/client/ayon_maya/api/alembic.py
similarity index 86%
rename from client/ayon_core/hosts/maya/api/alembic.py
rename to server_addon/maya/client/ayon_maya/api/alembic.py
index bf887df4c7..007e3ce4f3 100644
--- a/client/ayon_core/hosts/maya/api/alembic.py
+++ b/server_addon/maya/client/ayon_maya/api/alembic.py
@@ -4,7 +4,7 @@ import os
from maya import cmds # noqa
-from ayon_core.hosts.maya.api.lib import evaluation
+from ayon_maya.api.lib import evaluation
log = logging.getLogger(__name__)
@@ -22,7 +22,6 @@ ALEMBIC_ARGS = {
"melPostJobCallback": str,
"noNormals": bool,
"preRoll": bool,
- "preRollStartFrame": int,
"pythonPerFrameCallback": str,
"pythonPostJobCallback": str,
"renderableOnly": bool,
@@ -54,15 +53,22 @@ def extract_alembic(
endFrame=None,
eulerFilter=True,
frameRange="",
+ melPerFrameCallback=None,
+ melPostJobCallback=None,
noNormals=False,
preRoll=False,
preRollStartFrame=0,
+ pythonPerFrameCallback=None,
+ pythonPostJobCallback=None,
renderableOnly=False,
root=None,
selection=True,
startFrame=None,
step=1.0,
stripNamespaces=True,
+ userAttr=None,
+ userAttrPrefix=None,
+ uvsOnly=False,
uvWrite=True,
verbose=False,
wholeFrameGeo=False,
@@ -102,6 +108,11 @@ def extract_alembic(
string formatted as: "startFrame endFrame". This argument
overrides `startFrame` and `endFrame` arguments.
+ melPerFrameCallback (Optional[str]): MEL callback run per frame.
+
+ melPostJobCallback (Optional[str]): MEL callback after last frame is
+ written.
+
noNormals (bool): When on, normal data from the original polygon
objects is not included in the exported Alembic cache file.
@@ -113,6 +124,11 @@ def extract_alembic(
dependent translations and can be used to evaluate run-up that
isn't actually translated. Defaults to 0.
+ pythonPerFrameCallback (Optional[str]): Python callback run per frame.
+
+ pythonPostJobCallback (Optional[str]): Python callback after last frame
+ is written.
+
renderableOnly (bool): When on, any non-renderable nodes or hierarchy,
such as hidden objects, are not included in the Alembic file.
Defaults to False.
@@ -137,6 +153,15 @@ def extract_alembic(
object with the namespace taco:foo:bar appears as bar in the
Alembic file.
+ userAttr (list of str, optional): A specific user defined attribute to
+ write out. Defaults to [].
+
+ userAttrPrefix (list of str, optional): Prefix filter for determining
+ which user defined attributes to write out. Defaults to [].
+
+ uvsOnly (bool): When on, only uv data for PolyMesh and SubD shapes
+ will be written to the Alembic file.
+
uvWrite (bool): When on, UV data from polygon meshes and subdivision
objects are written to the Alembic file. Only the current UV map is
included.
@@ -183,6 +208,8 @@ def extract_alembic(
# Ensure list arguments are valid.
attr = attr or []
attrPrefix = attrPrefix or []
+ userAttr = userAttr or []
+ userAttrPrefix = userAttrPrefix or []
root = root or []
# Pass the start and end frame on as `frameRange` so that it
@@ -213,8 +240,10 @@ def extract_alembic(
"eulerFilter": eulerFilter,
"noNormals": noNormals,
"preRoll": preRoll,
+ "root": root,
"renderableOnly": renderableOnly,
"uvWrite": uvWrite,
+ "uvsOnly": uvsOnly,
"writeColorSets": writeColorSets,
"writeFaceSets": writeFaceSets,
"wholeFrameGeo": wholeFrameGeo,
@@ -226,9 +255,10 @@ def extract_alembic(
"step": step,
"attr": attr,
"attrPrefix": attrPrefix,
+ "userAttr": userAttr,
+ "userAttrPrefix": userAttrPrefix,
"stripNamespaces": stripNamespaces,
- "verbose": verbose,
- "preRollStartFrame": preRollStartFrame
+ "verbose": verbose
}
# Validate options
@@ -264,6 +294,17 @@ def extract_alembic(
if maya_version >= 2018:
options['autoSubd'] = options.pop('writeCreases', False)
+ # Only add callbacks if they are set so that we're not passing `None`
+ callbacks = {
+ "melPerFrameCallback": melPerFrameCallback,
+ "melPostJobCallback": melPostJobCallback,
+ "pythonPerFrameCallback": pythonPerFrameCallback,
+ "pythonPostJobCallback": pythonPostJobCallback,
+ }
+ for key, callback in callbacks.items():
+ if callback:
+ options[key] = str(callback)
+
# Format the job string from options
job_args = list()
for key, value in options.items():
@@ -297,7 +338,11 @@ def extract_alembic(
# exports are made. (PLN-31)
# TODO: Make sure this actually fixes the issues
with evaluation("off"):
- cmds.AbcExport(j=job_str, verbose=verbose)
+ cmds.AbcExport(
+ j=job_str,
+ verbose=verbose,
+ preRollStartFrame=preRollStartFrame
+ )
if verbose:
log.debug("Extracted Alembic to: %s", file)
diff --git a/client/ayon_core/hosts/maya/api/commands.py b/server_addon/maya/client/ayon_maya/api/commands.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/commands.py
rename to server_addon/maya/client/ayon_maya/api/commands.py
diff --git a/client/ayon_core/hosts/maya/api/customize.py b/server_addon/maya/client/ayon_maya/api/customize.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/customize.py
rename to server_addon/maya/client/ayon_maya/api/customize.py
diff --git a/client/ayon_core/hosts/maya/api/exitstack.py b/server_addon/maya/client/ayon_maya/api/exitstack.py
similarity index 98%
rename from client/ayon_core/hosts/maya/api/exitstack.py
rename to server_addon/maya/client/ayon_maya/api/exitstack.py
index 5eb7e15784..c35724e889 100644
--- a/client/ayon_core/hosts/maya/api/exitstack.py
+++ b/server_addon/maya/client/ayon_maya/api/exitstack.py
@@ -6,7 +6,7 @@ compatible implementation to support bothPython 2 and Python 3.
Instead of using ExitStack from contextlib, use it from this module:
->>> from ayon_core.hosts.maya.api.exitstack import ExitStack
+>>> from ayon_maya.api.exitstack import ExitStack
It will provide the appropriate ExitStack implementation for the current
running Python version.
diff --git a/client/ayon_core/hosts/maya/api/fbx.py b/server_addon/maya/client/ayon_maya/api/fbx.py
similarity index 97%
rename from client/ayon_core/hosts/maya/api/fbx.py
rename to server_addon/maya/client/ayon_maya/api/fbx.py
index 939da4011b..28a4058551 100644
--- a/client/ayon_core/hosts/maya/api/fbx.py
+++ b/server_addon/maya/client/ayon_maya/api/fbx.py
@@ -4,7 +4,7 @@ import logging
from maya import cmds # noqa
import maya.mel as mel # noqa
-from ayon_core.hosts.maya.api.lib import maintained_selection
+from ayon_maya.api.lib import maintained_selection
class FBXExtractor:
@@ -47,7 +47,7 @@ class FBXExtractor:
"smoothMesh": bool,
"instances": bool,
# "referencedContainersContent": bool, # deprecated in Maya 2016+
- "bakeComplexAnimation": int,
+ "bakeComplexAnimation": bool,
"bakeComplexStart": int,
"bakeComplexEnd": int,
"bakeComplexStep": int,
@@ -59,6 +59,7 @@ class FBXExtractor:
"constraints": bool,
"lights": bool,
"embeddedTextures": bool,
+ "includeChildren": bool,
"inputConnections": bool,
"upAxis": str, # x, y or z,
"triangulate": bool,
@@ -102,6 +103,7 @@ class FBXExtractor:
"constraints": False,
"lights": True,
"embeddedTextures": False,
+ "includeChildren": True,
"inputConnections": True,
"upAxis": "y",
"triangulate": False,
diff --git a/client/ayon_core/hosts/maya/api/gltf.py b/server_addon/maya/client/ayon_maya/api/gltf.py
similarity index 97%
rename from client/ayon_core/hosts/maya/api/gltf.py
rename to server_addon/maya/client/ayon_maya/api/gltf.py
index 2a983f1573..9aa4bf37ef 100644
--- a/client/ayon_core/hosts/maya/api/gltf.py
+++ b/server_addon/maya/client/ayon_maya/api/gltf.py
@@ -16,7 +16,7 @@ _gltf_options = {
"hbu": bool, # hashBufferURI
"ext": bool, # externalTextures
"ivt": int, # initialValuesTime
- "acn": str, # animationClipName
+ "acn": str, # animationClipName # codespell:ignore acn
"ast": int, # animationClipStartTime
"aet": int, # animationClipEndTime
"afr": float, # animationClipFrameRate
diff --git a/client/ayon_core/hosts/maya/api/lib.py b/server_addon/maya/client/ayon_maya/api/lib.py
similarity index 99%
rename from client/ayon_core/hosts/maya/api/lib.py
rename to server_addon/maya/client/ayon_maya/api/lib.py
index 89efc69ba8..2b41ffc06c 100644
--- a/client/ayon_core/hosts/maya/api/lib.py
+++ b/server_addon/maya/client/ayon_maya/api/lib.py
@@ -1299,7 +1299,7 @@ def is_visible(node,
override_enabled = cmds.getAttr('{}.overrideEnabled'.format(node))
override_visibility = cmds.getAttr('{}.overrideVisibility'.format(
node))
- if override_enabled and override_visibility:
+ if override_enabled and not override_visibility:
return False
if parentHidden:
diff --git a/client/ayon_core/hosts/maya/api/lib_renderproducts.py b/server_addon/maya/client/ayon_maya/api/lib_renderproducts.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/lib_renderproducts.py
rename to server_addon/maya/client/ayon_maya/api/lib_renderproducts.py
diff --git a/client/ayon_core/hosts/maya/api/lib_rendersettings.py b/server_addon/maya/client/ayon_maya/api/lib_rendersettings.py
similarity index 99%
rename from client/ayon_core/hosts/maya/api/lib_rendersettings.py
rename to server_addon/maya/client/ayon_maya/api/lib_rendersettings.py
index f9e243146a..f7f3f1d746 100644
--- a/client/ayon_core/hosts/maya/api/lib_rendersettings.py
+++ b/server_addon/maya/client/ayon_maya/api/lib_rendersettings.py
@@ -8,7 +8,7 @@ from ayon_core.settings import get_project_settings
from ayon_core.pipeline import CreatorError, get_current_project_name
from ayon_core.pipeline.context_tools import get_current_folder_entity
-from ayon_core.hosts.maya.api.lib import reset_frame_range
+from ayon_maya.api.lib import reset_frame_range
class RenderSettings(object):
diff --git a/client/ayon_core/hosts/maya/api/lib_rendersetup.py b/server_addon/maya/client/ayon_maya/api/lib_rendersetup.py
similarity index 99%
rename from client/ayon_core/hosts/maya/api/lib_rendersetup.py
rename to server_addon/maya/client/ayon_maya/api/lib_rendersetup.py
index 6dca8eb6dd..d93e6af0e2 100644
--- a/client/ayon_core/hosts/maya/api/lib_rendersetup.py
+++ b/server_addon/maya/client/ayon_maya/api/lib_rendersetup.py
@@ -19,7 +19,7 @@ from maya.app.renderSetup.model.override import (
UniqueOverride
)
-from ayon_core.hosts.maya.api.lib import get_attribute
+from ayon_maya.api.lib import get_attribute
EXACT_MATCH = 0
PARENT_MATCH = 1
diff --git a/client/ayon_core/hosts/maya/api/menu.py b/server_addon/maya/client/ayon_maya/api/menu.py
similarity index 99%
rename from client/ayon_core/hosts/maya/api/menu.py
rename to server_addon/maya/client/ayon_maya/api/menu.py
index e3ef50cdc0..153aff07c3 100644
--- a/client/ayon_core/hosts/maya/api/menu.py
+++ b/server_addon/maya/client/ayon_maya/api/menu.py
@@ -15,7 +15,7 @@ from ayon_core.pipeline import (
)
from ayon_core.pipeline.workfile import BuildWorkfile
from ayon_core.tools.utils import host_tools
-from ayon_core.hosts.maya.api import lib, lib_rendersettings
+from ayon_maya.api import lib, lib_rendersettings
from .lib import get_main_window, IS_HEADLESS
from ..tools import show_look_assigner
diff --git a/client/ayon_core/hosts/maya/api/pipeline.py b/server_addon/maya/client/ayon_maya/api/pipeline.py
similarity index 99%
rename from client/ayon_core/hosts/maya/api/pipeline.py
rename to server_addon/maya/client/ayon_maya/api/pipeline.py
index 74d73e5f95..84268cc6f1 100644
--- a/client/ayon_core/hosts/maya/api/pipeline.py
+++ b/server_addon/maya/client/ayon_maya/api/pipeline.py
@@ -45,8 +45,8 @@ from ayon_core.pipeline.workfile.lock_workfile import (
is_workfile_locked,
is_workfile_lock_enabled
)
-from ayon_core.hosts.maya import MAYA_ROOT_DIR
-from ayon_core.hosts.maya.lib import create_workspace_mel
+from ayon_maya import MAYA_ROOT_DIR
+from ayon_maya.lib import create_workspace_mel
from . import menu, lib
from .workio import (
@@ -58,7 +58,7 @@ from .workio import (
current_file
)
-log = logging.getLogger("ayon_core.hosts.maya")
+log = logging.getLogger("ayon_maya")
PLUGINS_DIR = os.path.join(MAYA_ROOT_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
diff --git a/client/ayon_core/hosts/maya/api/plugin.py b/server_addon/maya/client/ayon_maya/api/plugin.py
similarity index 97%
rename from client/ayon_core/hosts/maya/api/plugin.py
rename to server_addon/maya/client/ayon_maya/api/plugin.py
index 6f8b74c906..b8d9748ef1 100644
--- a/client/ayon_core/hosts/maya/api/plugin.py
+++ b/server_addon/maya/client/ayon_maya/api/plugin.py
@@ -2,40 +2,41 @@ import json
import os
from abc import ABCMeta
+import ayon_api
import qargparse
import six
-import ayon_api
-from maya import cmds
-from maya.app.renderSetup.model import renderSetup
from ayon_core.lib import BoolDef, Logger
-from ayon_core.settings import get_project_settings
from ayon_core.pipeline import (
- AYON_INSTANCE_ID,
- AYON_CONTAINER_ID,
- AVALON_INSTANCE_ID,
AVALON_CONTAINER_ID,
+ AVALON_INSTANCE_ID,
+ AYON_CONTAINER_ID,
+ AYON_INSTANCE_ID,
Anatomy,
-
+ AutoCreator,
CreatedInstance,
Creator as NewCreator,
- AutoCreator,
- HiddenCreator,
-
CreatorError,
+ HiddenCreator,
LegacyCreator,
LoaderPlugin,
- get_representation_path,
get_current_project_name,
+ get_representation_path,
+ publish,
)
-from ayon_core.pipeline.load import LoadError
from ayon_core.pipeline.create import get_product_name
+from ayon_core.pipeline.load import LoadError
+from ayon_core.settings import get_project_settings
+from maya import cmds
+from maya.app.renderSetup.model import renderSetup
+from pyblish.api import ContextPlugin, InstancePlugin
from . import lib
from .lib import imprint, read
from .pipeline import containerise
log = Logger.get_logger()
+SETTINGS_CATEGORY = "maya"
def _get_attr(node, attr, default=None):
@@ -652,7 +653,7 @@ def get_load_color_for_product_type(product_type, settings=None):
class Loader(LoaderPlugin):
hosts = ["maya"]
-
+ settings_category = SETTINGS_CATEGORY
load_settings = {} # defined in settings
@classmethod
@@ -807,10 +808,9 @@ class ReferenceLoader(Loader):
raise NotImplementedError("Must be implemented by subclass")
def update(self, container, context):
+ from ayon_maya.api.lib import get_container_members
from maya import cmds
- from ayon_core.hosts.maya.api.lib import get_container_members
-
node = container["objectName"]
project_name = context["project"]["name"]
@@ -1025,3 +1025,30 @@ class ReferenceLoader(Loader):
AYON_CONTAINER_ID, AVALON_CONTAINER_ID
}:
cmds.sets(node, forceElement=container)
+
+
+class MayaLoader(LoaderPlugin):
+ """Base class for loader plugins."""
+
+ settings_category = SETTINGS_CATEGORY
+
+
+class MayaInstancePlugin(InstancePlugin):
+ """Base class for instance publish plugins."""
+
+ settings_category = SETTINGS_CATEGORY
+ hosts = ["maya"]
+
+
+class MayaContextPlugin(ContextPlugin):
+ """Base class for context publish plugins."""
+
+ settings_category = SETTINGS_CATEGORY
+ hosts = ["maya"]
+
+
+class MayaExtractorPlugin(publish.Extractor):
+ """Base class for extract plugins."""
+
+ settings_category = SETTINGS_CATEGORY
+ hosts = ["maya"]
diff --git a/client/ayon_core/hosts/maya/api/render_setup_tools.py b/server_addon/maya/client/ayon_maya/api/render_setup_tools.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/render_setup_tools.py
rename to server_addon/maya/client/ayon_maya/api/render_setup_tools.py
diff --git a/client/ayon_core/hosts/maya/api/setdress.py b/server_addon/maya/client/ayon_maya/api/setdress.py
similarity index 99%
rename from client/ayon_core/hosts/maya/api/setdress.py
rename to server_addon/maya/client/ayon_maya/api/setdress.py
index b1d5beb343..a130b93f4f 100644
--- a/client/ayon_core/hosts/maya/api/setdress.py
+++ b/server_addon/maya/client/ayon_maya/api/setdress.py
@@ -20,7 +20,7 @@ from ayon_core.pipeline import (
get_representation_path,
get_current_project_name,
)
-from ayon_core.hosts.maya.api.lib import (
+from ayon_maya.api.lib import (
matrix_equals,
unique_namespace,
get_container_transforms,
diff --git a/client/ayon_core/hosts/maya/api/workfile_template_builder.py b/server_addon/maya/client/ayon_maya/api/workfile_template_builder.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/workfile_template_builder.py
rename to server_addon/maya/client/ayon_maya/api/workfile_template_builder.py
diff --git a/client/ayon_core/hosts/maya/api/workio.py b/server_addon/maya/client/ayon_maya/api/workio.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/workio.py
rename to server_addon/maya/client/ayon_maya/api/workio.py
diff --git a/client/ayon_core/hosts/maya/api/yeti.py b/server_addon/maya/client/ayon_maya/api/yeti.py
similarity index 100%
rename from client/ayon_core/hosts/maya/api/yeti.py
rename to server_addon/maya/client/ayon_maya/api/yeti.py
diff --git a/client/ayon_core/hosts/maya/hooks/pre_auto_load_plugins.py b/server_addon/maya/client/ayon_maya/hooks/pre_auto_load_plugins.py
similarity index 100%
rename from client/ayon_core/hosts/maya/hooks/pre_auto_load_plugins.py
rename to server_addon/maya/client/ayon_maya/hooks/pre_auto_load_plugins.py
diff --git a/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py b/server_addon/maya/client/ayon_maya/hooks/pre_copy_mel.py
similarity index 92%
rename from client/ayon_core/hosts/maya/hooks/pre_copy_mel.py
rename to server_addon/maya/client/ayon_maya/hooks/pre_copy_mel.py
index 683b4c59c7..c3268b09ee 100644
--- a/client/ayon_core/hosts/maya/hooks/pre_copy_mel.py
+++ b/server_addon/maya/client/ayon_maya/hooks/pre_copy_mel.py
@@ -1,5 +1,5 @@
from ayon_applications import PreLaunchHook, LaunchTypes
-from ayon_core.hosts.maya.lib import create_workspace_mel
+from ayon_maya.lib import create_workspace_mel
class PreCopyMel(PreLaunchHook):
diff --git a/client/ayon_core/hosts/maya/hooks/pre_open_workfile_post_initialization.py b/server_addon/maya/client/ayon_maya/hooks/pre_open_workfile_post_initialization.py
similarity index 100%
rename from client/ayon_core/hosts/maya/hooks/pre_open_workfile_post_initialization.py
rename to server_addon/maya/client/ayon_maya/hooks/pre_open_workfile_post_initialization.py
diff --git a/client/ayon_core/hosts/maya/lib.py b/server_addon/maya/client/ayon_maya/lib.py
similarity index 100%
rename from client/ayon_core/hosts/maya/lib.py
rename to server_addon/maya/client/ayon_maya/lib.py
diff --git a/client/ayon_core/hosts/max/plugins/__init__.py b/server_addon/maya/client/ayon_maya/plugins/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/max/plugins/__init__.py
rename to server_addon/maya/client/ayon_maya/plugins/__init__.py
diff --git a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py b/server_addon/maya/client/ayon_maya/plugins/create/convert_legacy.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/create/convert_legacy.py
rename to server_addon/maya/client/ayon_maya/plugins/create/convert_legacy.py
index 81cf9613b4..8616413bdd 100644
--- a/client/ayon_core/hosts/maya/plugins/create/convert_legacy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/convert_legacy.py
@@ -1,8 +1,8 @@
import ayon_api
from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin
-from ayon_core.hosts.maya.api import plugin
-from ayon_core.hosts.maya.api.lib import read
+from ayon_maya.api import plugin
+from ayon_maya.api.lib import read
from maya import cmds
from maya.app.renderSetup.model import renderSetup
@@ -12,7 +12,7 @@ class MayaLegacyConvertor(ProductConvertorPlugin,
plugin.MayaCreatorBase):
"""Find and convert any legacy products in the scene.
- This Convertor will find all legacy products in the scene and will
+ This Converter will find all legacy products in the scene and will
transform them to the current system. Since the old products doesn't
retain any information about their original creators, the only mapping
we can do is based on their families.
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py b/server_addon/maya/client/ayon_maya/plugins/create/create_animation_pointcache.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_animation_pointcache.py
index 08d50a1ab8..ea4cdb57fe 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_animation_pointcache.py
@@ -1,12 +1,11 @@
from maya import cmds
-from ayon_core.hosts.maya.api import lib, plugin
+from ayon_maya.api import lib, plugin
from ayon_core.lib import (
BoolDef,
NumberDef,
)
-from ayon_core.pipeline import CreatedInstance
def _get_animation_attr_defs(cls):
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_arnold_scene_source.py b/server_addon/maya/client/ayon_maya/plugins/create/create_arnold_scene_source.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/create/create_arnold_scene_source.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_arnold_scene_source.py
index dc0ffb02c1..8ae2759628 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_arnold_scene_source.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_arnold_scene_source.py
@@ -1,4 +1,6 @@
-from ayon_core.hosts.maya.api import (
+from maya import cmds
+
+from ayon_maya.api import (
lib,
plugin
)
@@ -87,16 +89,24 @@ class CreateArnoldSceneSource(plugin.MayaCreator):
return defs
+
+class CreateArnoldSceneSourceProxy(CreateArnoldSceneSource):
+ """Arnold Scene Source Proxy
+
+ This product type facilitates working with proxy geometry in the viewport.
+ """
+
+ identifier = "io.openpype.creators.maya.assproxy"
+ label = "Arnold Scene Source Proxy"
+ product_type = "assProxy"
+ icon = "cube"
+
def create(self, product_name, instance_data, pre_create_data):
-
- from maya import cmds
-
instance = super(CreateArnoldSceneSource, self).create(
product_name, instance_data, pre_create_data
)
instance_node = instance.get("instance_node")
- content = cmds.sets(name=instance_node + "_content_SET", empty=True)
proxy = cmds.sets(name=instance_node + "_proxy_SET", empty=True)
- cmds.sets([content, proxy], forceElement=instance_node)
+ cmds.sets([proxy], forceElement=instance_node)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_assembly.py b/server_addon/maya/client/ayon_maya/plugins/create/create_assembly.py
similarity index 83%
rename from client/ayon_core/hosts/maya/plugins/create/create_assembly.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_assembly.py
index 92df125748..dff04f059e 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_assembly.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_assembly.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
class CreateAssembly(plugin.MayaCreator):
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_camera.py b/server_addon/maya/client/ayon_maya/plugins/create/create_camera.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/create/create_camera.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_camera.py
index 4b1265bd3b..393176f5dd 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_camera.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_camera.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_layout.py b/server_addon/maya/client/ayon_maya/plugins/create/create_layout.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/create/create_layout.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_layout.py
index 6cbc697502..1d9bc2c1c8 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_layout.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_layout.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
from ayon_core.lib import BoolDef
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_look.py b/server_addon/maya/client/ayon_maya/plugins/create/create_look.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/create/create_look.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_look.py
index ac3625c38f..1f90d18607 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_look.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_look.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
plugin,
lib
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_matchmove.py b/server_addon/maya/client/ayon_maya/plugins/create/create_matchmove.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/create/create_matchmove.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_matchmove.py
index 44443a8b9f..9cb2a3dd47 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_matchmove.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_matchmove.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_maya_usd.py b/server_addon/maya/client/ayon_maya/plugins/create/create_maya_usd.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/create/create_maya_usd.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_maya_usd.py
index 3f34a541b4..19b55384f3 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_maya_usd.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_maya_usd.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin, lib
+from ayon_maya.api import plugin, lib
from ayon_core.lib import (
BoolDef,
EnumDef,
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_mayascene.py b/server_addon/maya/client/ayon_maya/plugins/create/create_mayascene.py
similarity index 84%
rename from client/ayon_core/hosts/maya/plugins/create/create_mayascene.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_mayascene.py
index cfe46336a2..9913efc016 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_mayascene.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_mayascene.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
class CreateMayaScene(plugin.MayaCreator):
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_model.py b/server_addon/maya/client/ayon_maya/plugins/create/create_model.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/create/create_model.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_model.py
index b47df421f3..87696c58d2 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_model.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_model.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
from ayon_core.lib import (
BoolDef,
TextDef
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py b/server_addon/maya/client/ayon_maya/plugins/create/create_multishot_layout.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_multishot_layout.py
index 7216236719..5229823110 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_multishot_layout.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_multishot_layout.py
@@ -8,7 +8,7 @@ from ayon_api import (
)
from maya import cmds # noqa: F401
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
from ayon_core.lib import BoolDef, EnumDef, TextDef
from ayon_core.pipeline import (
Creator,
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_look.py b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_look.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/create/create_multiverse_look.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_look.py
index de604a33b3..f2dcb77187 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_look.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_look.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
from ayon_core.lib import (
BoolDef,
EnumDef
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd.py b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd.py
index 668700995f..bdcea4cd2c 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin, lib
+from ayon_maya.api import plugin, lib
from ayon_core.lib import (
BoolDef,
NumberDef,
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_comp.py b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd_comp.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_comp.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd_comp.py
index 120e6ad920..2459704d14 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_comp.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd_comp.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin, lib
+from ayon_maya.api import plugin, lib
from ayon_core.lib import (
BoolDef,
NumberDef,
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_over.py b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd_over.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_over.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd_over.py
index 26208794e3..b070daf550 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_multiverse_usd_over.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_multiverse_usd_over.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin, lib
+from ayon_maya.api import plugin, lib
from ayon_core.lib import (
BoolDef,
NumberDef,
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_proxy_abc.py b/server_addon/maya/client/ayon_maya/plugins/create/create_proxy_abc.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/create/create_proxy_abc.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_proxy_abc.py
index ecc031436c..431f113941 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_proxy_abc.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_proxy_abc.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_redshift_proxy.py b/server_addon/maya/client/ayon_maya/plugins/create/create_redshift_proxy.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/create/create_redshift_proxy.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_redshift_proxy.py
index d99fe5a787..c4cc874a2a 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_redshift_proxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_redshift_proxy.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Creator of Redshift proxy product types."""
-from ayon_core.hosts.maya.api import plugin, lib
+from ayon_maya.api import plugin, lib
from ayon_core.lib import BoolDef
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_render.py b/server_addon/maya/client/ayon_maya/plugins/create/create_render.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/create/create_render.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_render.py
index e5a8d4dbd8..5defee7d07 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_render.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_render.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Create ``Render`` instance in Maya."""
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib_rendersettings,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_rendersetup.py b/server_addon/maya/client/ayon_maya/plugins/create/create_rendersetup.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/create/create_rendersetup.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_rendersetup.py
index 3d8d6a7309..415ab4ff8c 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_rendersetup.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_rendersetup.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
from ayon_core.pipeline import CreatorError
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_review.py b/server_addon/maya/client/ayon_maya/plugins/create/create_review.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/create/create_review.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_review.py
index 8a2f2df745..26fad91ed9 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_review.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_review.py
@@ -3,7 +3,7 @@ import json
from maya import cmds
import ayon_api
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_rig.py b/server_addon/maya/client/ayon_maya/plugins/create/create_rig.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/create/create_rig.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_rig.py
index 54be50c169..135e51bcbf 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_rig.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_rig.py
@@ -1,6 +1,6 @@
from maya import cmds
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
class CreateRig(plugin.MayaCreator):
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_setdress.py b/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/create/create_setdress.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py
index 0f72d4d184..12532e0724 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_setdress.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
from ayon_core.lib import BoolDef
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py b/server_addon/maya/client/ayon_maya/plugins/create/create_unreal_skeletalmesh.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_unreal_skeletalmesh.py
index a32e94971e..a182fe7a24 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_skeletalmesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_unreal_skeletalmesh.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator for Unreal Skeletal Meshes."""
-from ayon_core.hosts.maya.api import plugin, lib
+from ayon_maya.api import plugin, lib
from ayon_core.lib import (
BoolDef,
TextDef
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py b/server_addon/maya/client/ayon_maya/plugins/create/create_unreal_staticmesh.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_unreal_staticmesh.py
index 76c33f00cc..e5436bca64 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_staticmesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_unreal_staticmesh.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Creator for Unreal Static Meshes."""
-from ayon_core.hosts.maya.api import plugin, lib
+from ayon_maya.api import plugin, lib
from maya import cmds # noqa
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py b/server_addon/maya/client/ayon_maya/plugins/create/create_unreal_yeticache.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_unreal_yeticache.py
index dea64b40fb..eea866d406 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_unreal_yeticache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_unreal_yeticache.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_vrayproxy.py b/server_addon/maya/client/ayon_maya/plugins/create/create_vrayproxy.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/create/create_vrayproxy.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_vrayproxy.py
index d565ec37e0..742e14ace0 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_vrayproxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_vrayproxy.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
plugin,
lib
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_vrayscene.py b/server_addon/maya/client/ayon_maya/plugins/create/create_vrayscene.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/create/create_vrayscene.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_vrayscene.py
index cf5e7b5364..11c356fdef 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_vrayscene.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_vrayscene.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Create instance of vrayscene."""
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib_rendersettings,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py b/server_addon/maya/client/ayon_maya/plugins/create/create_workfile.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/create/create_workfile.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_workfile.py
index f636ed7b74..e0c94611b0 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_workfile.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_workfile.py
@@ -3,7 +3,7 @@
import ayon_api
from ayon_core.pipeline import CreatedInstance, AutoCreator
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
from maya import cmds
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_xgen.py b/server_addon/maya/client/ayon_maya/plugins/create/create_xgen.py
similarity index 79%
rename from client/ayon_core/hosts/maya/plugins/create/create_xgen.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_xgen.py
index fec2f07456..d13d032a13 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_xgen.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_xgen.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import plugin
+from ayon_maya.api import plugin
class CreateXgen(plugin.MayaCreator):
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_yeti_cache.py b/server_addon/maya/client/ayon_maya/plugins/create/create_yeti_cache.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/create/create_yeti_cache.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_yeti_cache.py
index bf20acaca8..8a834f18c0 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_yeti_cache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_yeti_cache.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/create/create_yeti_rig.py b/server_addon/maya/client/ayon_maya/plugins/create/create_yeti_rig.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/create/create_yeti_rig.py
rename to server_addon/maya/client/ayon_maya/plugins/create/create_yeti_rig.py
index dfe224ceb1..c5378dc1b8 100644
--- a/client/ayon_core/hosts/maya/plugins/create/create_yeti_rig.py
+++ b/server_addon/maya/client/ayon_maya/plugins/create/create_yeti_rig.py
@@ -1,6 +1,6 @@
from maya import cmds
-from ayon_core.hosts.maya.api import (
+from ayon_maya.api import (
lib,
plugin
)
diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py b/server_addon/maya/client/ayon_maya/plugins/inventory/connect_geometry.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py
rename to server_addon/maya/client/ayon_maya/plugins/inventory/connect_geometry.py
index 5410546a2e..ccb88313e9 100644
--- a/client/ayon_core/hosts/maya/plugins/inventory/connect_geometry.py
+++ b/server_addon/maya/client/ayon_maya/plugins/inventory/connect_geometry.py
@@ -1,7 +1,7 @@
from maya import cmds
from ayon_core.pipeline import InventoryAction, get_repres_contexts
-from ayon_core.hosts.maya.api.lib import get_id
+from ayon_maya.api.lib import get_id
class ConnectGeometry(InventoryAction):
diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py b/server_addon/maya/client/ayon_maya/plugins/inventory/connect_xgen.py
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/inventory/connect_xgen.py
rename to server_addon/maya/client/ayon_maya/plugins/inventory/connect_xgen.py
diff --git a/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py b/server_addon/maya/client/ayon_maya/plugins/inventory/connect_yeti_rig.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py
rename to server_addon/maya/client/ayon_maya/plugins/inventory/connect_yeti_rig.py
index 8f13cc6ae5..2385444403 100644
--- a/client/ayon_core/hosts/maya/plugins/inventory/connect_yeti_rig.py
+++ b/server_addon/maya/client/ayon_maya/plugins/inventory/connect_yeti_rig.py
@@ -9,7 +9,7 @@ from ayon_core.pipeline import (
get_repres_contexts,
get_representation_path,
)
-from ayon_core.hosts.maya.api.lib import get_container_members, get_id
+from ayon_maya.api.lib import get_container_members, get_id
class ConnectYetiRig(InventoryAction):
diff --git a/client/ayon_core/hosts/maya/plugins/inventory/import_modelrender.py b/server_addon/maya/client/ayon_maya/plugins/inventory/import_modelrender.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/inventory/import_modelrender.py
rename to server_addon/maya/client/ayon_maya/plugins/inventory/import_modelrender.py
index 4655017ae5..5e36ec6bc1 100644
--- a/client/ayon_core/hosts/maya/plugins/inventory/import_modelrender.py
+++ b/server_addon/maya/client/ayon_maya/plugins/inventory/import_modelrender.py
@@ -8,7 +8,7 @@ from ayon_core.pipeline import (
InventoryAction,
get_current_project_name,
)
-from ayon_core.hosts.maya.api.lib import (
+from ayon_maya.api.lib import (
maintained_selection,
apply_shaders
)
diff --git a/client/ayon_core/hosts/maya/plugins/inventory/import_reference.py b/server_addon/maya/client/ayon_maya/plugins/inventory/import_reference.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/inventory/import_reference.py
rename to server_addon/maya/client/ayon_maya/plugins/inventory/import_reference.py
index 771cb96a57..5e42facad4 100644
--- a/client/ayon_core/hosts/maya/plugins/inventory/import_reference.py
+++ b/server_addon/maya/client/ayon_maya/plugins/inventory/import_reference.py
@@ -1,7 +1,7 @@
from maya import cmds
from ayon_core.pipeline import InventoryAction
-from ayon_core.hosts.maya.api.lib import get_reference_node
+from ayon_maya.api.lib import get_reference_node
class ImportReference(InventoryAction):
diff --git a/client/ayon_core/hosts/maya/plugins/inventory/rig_recreate_animation_instance.py b/server_addon/maya/client/ayon_maya/plugins/inventory/rig_recreate_animation_instance.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/inventory/rig_recreate_animation_instance.py
rename to server_addon/maya/client/ayon_maya/plugins/inventory/rig_recreate_animation_instance.py
index cbff293cd7..796a651f8a 100644
--- a/client/ayon_core/hosts/maya/plugins/inventory/rig_recreate_animation_instance.py
+++ b/server_addon/maya/client/ayon_maya/plugins/inventory/rig_recreate_animation_instance.py
@@ -3,7 +3,7 @@ from ayon_core.pipeline import (
get_current_project_name,
)
from ayon_core.pipeline.load import get_representation_contexts_by_ids
-from ayon_core.hosts.maya.api.lib import (
+from ayon_maya.api.lib import (
create_rig_animation_instance,
get_container_members,
)
diff --git a/client/ayon_core/hosts/maya/plugins/inventory/select_containers.py b/server_addon/maya/client/ayon_maya/plugins/inventory/select_containers.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/inventory/select_containers.py
rename to server_addon/maya/client/ayon_maya/plugins/inventory/select_containers.py
index f0bb2fc376..e45c8a5706 100644
--- a/client/ayon_core/hosts/maya/plugins/inventory/select_containers.py
+++ b/server_addon/maya/client/ayon_maya/plugins/inventory/select_containers.py
@@ -1,7 +1,7 @@
from maya import cmds
from ayon_core.pipeline import InventoryAction, registered_host
-from ayon_core.hosts.maya.api.lib import get_container_members
+from ayon_maya.api.lib import get_container_members
class SelectInScene(InventoryAction):
diff --git a/client/ayon_core/hosts/maya/plugins/load/_load_animation.py b/server_addon/maya/client/ayon_maya/plugins/load/_load_animation.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/load/_load_animation.py
rename to server_addon/maya/client/ayon_maya/plugins/load/_load_animation.py
index 393f6b0115..6d4ebe250c 100644
--- a/client/ayon_core/hosts/maya/plugins/load/_load_animation.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/_load_animation.py
@@ -1,4 +1,4 @@
-import ayon_core.hosts.maya.api.plugin
+import ayon_maya.api.plugin
import maya.cmds as cmds
@@ -14,7 +14,7 @@ def _process_reference(file_url, name, namespace, options):
Returns:
list: list of object nodes
"""
- from ayon_core.hosts.maya.api.lib import unique_namespace
+ from ayon_maya.api.lib import unique_namespace
# Get name from asset being loaded
# Assuming name is product name from the animation, we split the number
# suffix from the name to ensure the namespace is unique
@@ -43,7 +43,7 @@ def _process_reference(file_url, name, namespace, options):
return nodes
-class AbcLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
+class AbcLoader(ayon_maya.api.plugin.ReferenceLoader):
"""Loader to reference an Alembic file"""
product_types = {
@@ -74,7 +74,7 @@ class AbcLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
return nodes
-class FbxLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
+class FbxLoader(ayon_maya.api.plugin.ReferenceLoader):
"""Loader to reference an Fbx files"""
product_types = {
diff --git a/client/ayon_core/hosts/maya/plugins/load/actions.py b/server_addon/maya/client/ayon_maya/plugins/load/actions.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/load/actions.py
rename to server_addon/maya/client/ayon_maya/plugins/load/actions.py
index 8bef219812..d28645ea43 100644
--- a/client/ayon_core/hosts/maya/plugins/load/actions.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/actions.py
@@ -3,11 +3,11 @@
"""
import qargparse
from ayon_core.pipeline import load
-from ayon_core.hosts.maya.api.lib import (
+from ayon_maya.api.lib import (
maintained_selection,
get_custom_namespace
)
-import ayon_core.hosts.maya.api.plugin
+import ayon_maya.api.plugin
class SetFrameRangeLoader(load.LoaderPlugin):
@@ -85,7 +85,7 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
animationEndTime=end)
-class ImportMayaLoader(ayon_core.hosts.maya.api.plugin.Loader):
+class ImportMayaLoader(ayon_maya.api.plugin.Loader):
"""Import action for Maya (unmanaged)
Warning:
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py b/server_addon/maya/client/ayon_maya/plugins/load/load_arnold_standin.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_arnold_standin.py
index 4b7d2f42ab..d01cea3ad4 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_arnold_standin.py
@@ -1,20 +1,18 @@
import os
+
import clique
-
import maya.cmds as cmds
-
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.lib import (
- unique_namespace,
+from ayon_maya.api.lib import (
get_attribute_input,
+ get_fps_for_current_context,
maintained_selection,
+ unique_namespace,
)
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api import plugin
def is_sequence(files):
@@ -25,11 +23,17 @@ def is_sequence(files):
return sequence
-class ArnoldStandinLoader(load.LoaderPlugin):
+class ArnoldStandinLoader(plugin.Loader):
"""Load as Arnold standin"""
product_types = {
- "ass", "animation", "model", "proxyAbc", "pointcache", "usd"
+ "ass",
+ "assProxy",
+ "animation",
+ "model",
+ "proxyAbc",
+ "pointcache",
+ "usd"
}
representations = {"ass", "abc", "usda", "usdc", "usd"}
@@ -95,8 +99,10 @@ class ArnoldStandinLoader(load.LoaderPlugin):
sequence = is_sequence(os.listdir(os.path.dirname(repre_path)))
cmds.setAttr(standin_shape + ".useFrameExtension", sequence)
- fps = float(version_attributes.get("fps")) or 25
- cmds.setAttr(standin_shape + ".abcFPS", fps)
+ fps = (
+ version_attributes.get("fps") or get_fps_for_current_context()
+ )
+ cmds.setAttr(standin_shape + ".abcFPS", float(fps))
nodes = [root, standin, standin_shape]
if operator is not None:
@@ -128,6 +134,18 @@ class ArnoldStandinLoader(load.LoaderPlugin):
proxy_path = "/".join([os.path.dirname(path), proxy_basename])
return proxy_basename, proxy_path
+ def _update_operators(self, string_replace_operator, proxy_basename, path):
+ cmds.setAttr(
+ string_replace_operator + ".match",
+ proxy_basename.split(".")[0],
+ type="string"
+ )
+ cmds.setAttr(
+ string_replace_operator + ".replace",
+ os.path.basename(path).split(".")[0],
+ type="string"
+ )
+
def _setup_proxy(self, shape, path, namespace):
proxy_basename, proxy_path = self._get_proxy_path(path)
@@ -150,16 +168,7 @@ class ArnoldStandinLoader(load.LoaderPlugin):
"*.(@node=='{}')".format(node_type),
type="string"
)
- cmds.setAttr(
- string_replace_operator + ".match",
- proxy_basename,
- type="string"
- )
- cmds.setAttr(
- string_replace_operator + ".replace",
- os.path.basename(path),
- type="string"
- )
+ self._update_operators(string_replace_operator, proxy_basename, path)
cmds.connectAttr(
string_replace_operator + ".out",
@@ -194,18 +203,9 @@ class ArnoldStandinLoader(load.LoaderPlugin):
path = get_representation_path(repre_entity)
proxy_basename, proxy_path = self._get_proxy_path(path)
- # Whether there is proxy or so, we still update the string operator.
+ # Whether there is proxy or not, we still update the string operator.
# If no proxy exists, the string operator won't replace anything.
- cmds.setAttr(
- string_replace_operator + ".match",
- proxy_basename,
- type="string"
- )
- cmds.setAttr(
- string_replace_operator + ".replace",
- os.path.basename(path),
- type="string"
- )
+ self._update_operators(string_replace_operator, proxy_basename, path)
dso_path = path
if os.path.exists(proxy_path):
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_as_template.py b/server_addon/maya/client/ayon_maya/plugins/load/load_as_template.py
similarity index 79%
rename from client/ayon_core/hosts/maya/plugins/load/load_as_template.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_as_template.py
index f696d369e3..b6bd3c3ab6 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_as_template.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_as_template.py
@@ -1,16 +1,10 @@
-from ayon_core.lib import (
- BoolDef
-)
-from ayon_core.pipeline import (
- load,
- registered_host
-)
-from ayon_core.hosts.maya.api.workfile_template_builder import (
- MayaTemplateBuilder
-)
+from ayon_core.lib import BoolDef
+from ayon_core.pipeline import registered_host
+from ayon_maya.api import plugin
+from ayon_maya.api.workfile_template_builder import MayaTemplateBuilder
-class LoadAsTemplate(load.LoaderPlugin):
+class LoadAsTemplate(plugin.Loader):
"""Load workfile as a template """
product_types = {"workfile", "mayaScene"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_assembly.py b/server_addon/maya/client/ayon_maya/plugins/load/load_assembly.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/load/load_assembly.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_assembly.py
index 0fcbc6bd07..490631aa67 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_assembly.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_assembly.py
@@ -1,16 +1,12 @@
import maya.cmds as cmds
-
-from ayon_core.pipeline import (
- load,
- remove_container
-)
-
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.lib import unique_namespace
-from ayon_core.hosts.maya.api import setdress
+from ayon_core.pipeline import remove_container
+from ayon_maya.api import setdress
+from ayon_maya.api.lib import unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
-class AssemblyLoader(load.LoaderPlugin):
+class AssemblyLoader(plugin.Loader):
product_types = {"assembly"}
representations = {"json"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_audio.py b/server_addon/maya/client/ayon_maya/plugins/load/load_audio.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/load/load_audio.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_audio.py
index 228189f1a1..d9f67fdd90 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_audio.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_audio.py
@@ -1,14 +1,11 @@
+from ayon_core.pipeline import get_representation_path
+from ayon_maya.api.lib import get_container_members, unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
from maya import cmds, mel
-from ayon_core.pipeline import (
- load,
- get_representation_path,
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.lib import unique_namespace, get_container_members
-
-class AudioLoader(load.LoaderPlugin):
+class AudioLoader(plugin.Loader):
"""Specific loader of audio."""
product_types = {"audio"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py b/server_addon/maya/client/ayon_maya/plugins/load/load_gpucache.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/load/load_gpucache.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_gpucache.py
index 9832d2d657..795d01fd5a 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_gpucache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_gpucache.py
@@ -1,16 +1,13 @@
import maya.cmds as cmds
-
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.lib import unique_namespace
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api.lib import unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
+from ayon_maya.api.plugin import get_load_color_for_product_type
-class GpuCacheLoader(load.LoaderPlugin):
+class GpuCacheLoader(plugin.Loader):
"""Load Alembic as gpuCache"""
product_types = {"model", "animation", "proxyAbc", "pointcache"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image.py b/server_addon/maya/client/ayon_maya/plugins/load/load_image.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/load/load_image.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_image.py
index 5b0858ce70..453e24a2d5 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_image.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_image.py
@@ -1,24 +1,17 @@
import copy
from ayon_core.lib import EnumDef
-from ayon_core.pipeline import (
- load,
- get_current_host_name,
+from ayon_core.pipeline import get_current_host_name
+from ayon_core.pipeline.colorspace import (
+ get_current_context_imageio_config_preset,
+ get_imageio_file_rules,
+ get_imageio_file_rules_colorspace_from_filepath,
)
from ayon_core.pipeline.load.utils import get_representation_path_from_context
-from ayon_core.pipeline.colorspace import (
- get_imageio_file_rules_colorspace_from_filepath,
- get_imageio_config,
- get_imageio_file_rules
-)
from ayon_core.settings import get_project_settings
-
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.lib import (
- unique_namespace,
- namespaced
-)
-
+from ayon_maya.api.lib import namespaced, unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
from maya import cmds
@@ -88,7 +81,7 @@ def create_stencil():
return file, place, stencil, place_stencil
-class FileNodeLoader(load.LoaderPlugin):
+class FileNodeLoader(plugin.Loader):
"""File node loader."""
product_types = {"image", "plate", "render"}
@@ -270,8 +263,7 @@ class FileNodeLoader(load.LoaderPlugin):
host_name = get_current_host_name()
project_settings = get_project_settings(project_name)
- config_data = get_imageio_config(
- project_name, host_name,
+ config_data = get_current_context_imageio_config_preset(
project_settings=project_settings
)
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py b/server_addon/maya/client/ayon_maya/plugins/load/load_image_plane.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/load/load_image_plane.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_image_plane.py
index 15c7654c52..3da67221e2 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_image_plane.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_image_plane.py
@@ -1,18 +1,14 @@
-from qtpy import QtWidgets, QtCore
-
-from ayon_core.pipeline import (
- load,
- get_representation_path,
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.lib import (
- unique_namespace,
+from ayon_core.pipeline import get_representation_path
+from ayon_maya.api.lib import (
+ get_container_members,
namespaced,
pairwise,
- get_container_members
+ unique_namespace,
)
-
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
from maya import cmds
+from qtpy import QtCore, QtWidgets
def disconnect_inputs(plug):
@@ -84,7 +80,7 @@ class CameraWindow(QtWidgets.QDialog):
self.close()
-class ImagePlaneLoader(load.LoaderPlugin):
+class ImagePlaneLoader(plugin.Loader):
"""Specific loader of plate for image planes on selected camera."""
product_types = {"image", "plate", "render"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_look.py b/server_addon/maya/client/ayon_maya/plugins/load/load_look.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/load/load_look.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_look.py
index af0e000dd2..da7b3691fd 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_look.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_look.py
@@ -3,18 +3,16 @@
import json
from collections import defaultdict
-from qtpy import QtWidgets
+import ayon_maya.api.plugin
from ayon_api import get_representation_by_name
-
from ayon_core.pipeline import get_representation_path
-import ayon_core.hosts.maya.api.plugin
-from ayon_core.hosts.maya.api import lib
-from ayon_core.hosts.maya.api.lib import get_reference_node
-
from ayon_core.tools.utils import ScrollMessageBox
+from ayon_maya.api import lib
+from ayon_maya.api.lib import get_reference_node
+from qtpy import QtWidgets
-class LookLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
+class LookLoader(ayon_maya.api.plugin.ReferenceLoader):
"""Specific loader for lookdev"""
product_types = {"look"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_matchmove.py b/server_addon/maya/client/ayon_maya/plugins/load/load_matchmove.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/load/load_matchmove.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_matchmove.py
index b19b14b1aa..7689a3ca5e 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_matchmove.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_matchmove.py
@@ -1,7 +1,8 @@
+from ayon_maya.api import plugin
from maya import mel
-from ayon_core.pipeline import load
-class MatchmoveLoader(load.LoaderPlugin):
+
+class MatchmoveLoader(plugin.Loader):
"""
This will run matchmove script to create track in scene.
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py b/server_addon/maya/client/ayon_maya/plugins/load/load_maya_usd.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_maya_usd.py
index 628a25e574..79fc1fc94f 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_maya_usd.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_maya_usd.py
@@ -1,19 +1,13 @@
# -*- coding: utf-8 -*-
import maya.cmds as cmds
-
-from ayon_core.pipeline import (
- load,
- get_representation_path,
-)
+from ayon_core.pipeline import get_representation_path
from ayon_core.pipeline.load import get_representation_path_from_context
-from ayon_core.hosts.maya.api.lib import (
- namespaced,
- unique_namespace
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
+from ayon_maya.api.lib import namespaced, unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
-class MayaUsdLoader(load.LoaderPlugin):
+class MayaUsdLoader(plugin.Loader):
"""Read USD data in a Maya USD Proxy"""
product_types = {"model", "usd", "pointcache", "animation"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py b/server_addon/maya/client/ayon_maya/plugins/load/load_multiverse_usd.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_multiverse_usd.py
index f32c76481d..026740a957 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_multiverse_usd.py
@@ -1,23 +1,16 @@
# -*- coding: utf-8 -*-
-import maya.cmds as cmds
-from maya import mel
import os
+import maya.cmds as cmds
from ayon_api import get_representation_by_id
-
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection,
- namespaced,
- unique_namespace
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
+from ayon_core.pipeline import get_representation_path
+from ayon_maya.api import plugin
+from ayon_maya.api.lib import maintained_selection, namespaced, unique_namespace
+from ayon_maya.api.pipeline import containerise
+from maya import mel
-class MultiverseUsdLoader(load.LoaderPlugin):
+class MultiverseUsdLoader(plugin.Loader):
"""Read USD data in a Multiverse Compound"""
product_types = {
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py b/server_addon/maya/client/ayon_maya/plugins/load/load_multiverse_usd_over.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_multiverse_usd_over.py
index b23fa48f07..a8fff12577 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_multiverse_usd_over.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_multiverse_usd_over.py
@@ -1,22 +1,17 @@
# -*- coding: utf-8 -*-
-import maya.cmds as cmds
-from maya import mel
import os
+import maya.cmds as cmds
import qargparse
from ayon_api import get_representation_by_id
-
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
+from ayon_core.pipeline import get_representation_path
+from ayon_maya.api import plugin
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api.pipeline import containerise
+from maya import mel
-class MultiverseUsdOverLoader(load.LoaderPlugin):
+class MultiverseUsdOverLoader(plugin.Loader):
"""Reference file"""
product_types = {"mvUsdOverride"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py b/server_addon/maya/client/ayon_maya/plugins/load/load_redshift_proxy.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_redshift_proxy.py
index 7760d4081c..92bf6dfe26 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_redshift_proxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_redshift_proxy.py
@@ -1,25 +1,18 @@
# -*- coding: utf-8 -*-
"""Loader for Redshift proxy."""
import os
+
import clique
-
import maya.cmds as cmds
-
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.lib import (
- namespaced,
- maintained_selection,
- unique_namespace
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api import plugin
+from ayon_maya.api.lib import maintained_selection, namespaced, unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api.plugin import get_load_color_for_product_type
-class RedshiftProxyLoader(load.LoaderPlugin):
+class RedshiftProxyLoader(plugin.Loader):
"""Load Redshift proxy"""
product_types = {"redshiftproxy"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_reference.py b/server_addon/maya/client/ayon_maya/plugins/load/load_reference.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/load/load_reference.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_reference.py
index 847591bd11..f8e1062e38 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_reference.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_reference.py
@@ -1,17 +1,16 @@
-import difflib
import contextlib
+import difflib
-from maya import cmds
import qargparse
-
from ayon_core.settings import get_project_settings
-from ayon_core.hosts.maya.api import plugin
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection,
+from ayon_maya.api import plugin
+from ayon_maya.api.lib import (
+ create_rig_animation_instance,
get_container_members,
+ maintained_selection,
parent_nodes,
- create_rig_animation_instance
)
+from maya import cmds
@contextlib.contextmanager
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py b/server_addon/maya/client/ayon_maya/plugins/load/load_rendersetup.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_rendersetup.py
index d5685b2c4c..3b323698c4 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_rendersetup.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_rendersetup.py
@@ -6,21 +6,18 @@ When this json is loaded, it will overwrite all settings on RenderSetup
instance.
"""
+import contextlib
import json
import sys
-import six
-import contextlib
-from ayon_core.lib import BoolDef, EnumDef
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api import lib
-from ayon_core.hosts.maya.api.pipeline import containerise
-
-from maya import cmds
import maya.app.renderSetup.model.renderSetup as renderSetup
+import six
+from ayon_core.lib import BoolDef, EnumDef
+from ayon_core.pipeline import get_representation_path
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from ayon_maya.api.pipeline import containerise
+from maya import cmds
@contextlib.contextmanager
@@ -48,7 +45,7 @@ def mark_all_imported(enabled):
cmds.deleteAttr(plug)
-class RenderSetupLoader(load.LoaderPlugin):
+class RenderSetupLoader(plugin.Loader):
"""Load json preset for RenderSetup overwriting current one."""
product_types = {"rendersetup"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py b/server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_arnold.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_arnold.py
index 5b0c78fd6f..4515ec499d 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_arnold.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_arnold.py
@@ -1,15 +1,18 @@
+"""Load OpenVDB for Arnold in aiVolume.
+
+TODO:
+ `aiVolume` doesn't automatically set velocity fps correctly, set manual?
+
+"""
import os
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
-# TODO aiVolume doesn't automatically set velocity fps correctly, set manual?
+from ayon_maya.api import plugin
+from ayon_maya.api.plugin import get_load_color_for_product_type
-class LoadVDBtoArnold(load.LoaderPlugin):
+class LoadVDBtoArnold(plugin.Loader):
"""Load OpenVDB for Arnold in aiVolume"""
product_types = {"vdbcache"}
@@ -21,9 +24,9 @@ class LoadVDBtoArnold(load.LoaderPlugin):
def load(self, context, name, namespace, data):
+ from ayon_maya.api.lib import unique_namespace
+ from ayon_maya.api.pipeline import containerise
from maya import cmds
- from ayon_core.hosts.maya.api.pipeline import containerise
- from ayon_core.hosts.maya.api.lib import unique_namespace
product_type = context["product"]["productType"]
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py b/server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_redshift.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_redshift.py
index e345a7bf6f..c08004421b 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_redshift.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_redshift.py
@@ -1,14 +1,12 @@
import os
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api import plugin
+from ayon_maya.api.plugin import get_load_color_for_product_type
-class LoadVDBtoRedShift(load.LoaderPlugin):
+class LoadVDBtoRedShift(plugin.Loader):
"""Load OpenVDB in a Redshift Volume Shape
Note that the RedshiftVolumeShape is created without a RedshiftVolume
@@ -27,9 +25,9 @@ class LoadVDBtoRedShift(load.LoaderPlugin):
def load(self, context, name=None, namespace=None, data=None):
+ from ayon_maya.api.lib import unique_namespace
+ from ayon_maya.api.pipeline import containerise
from maya import cmds
- from ayon_core.hosts.maya.api.pipeline import containerise
- from ayon_core.hosts.maya.api.lib import unique_namespace
product_type = context["product"]["productType"]
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py b/server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_vray.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_vray.py
index d6d1c948b0..f022f8be5e 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_vdb_to_vray.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_vdb_to_vray.py
@@ -1,12 +1,9 @@
import os
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
-
+from ayon_maya.api import plugin
+from ayon_maya.api.plugin import get_load_color_for_product_type
from maya import cmds
# List of 3rd Party Channels Mapping names for VRayVolumeGrid
@@ -74,7 +71,7 @@ def _fix_duplicate_vvg_callbacks():
matched.add(callback)
-class LoadVDBtoVRay(load.LoaderPlugin):
+class LoadVDBtoVRay(plugin.Loader):
"""Load OpenVDB in a V-Ray Volume Grid"""
product_types = {"vdbcache"}
@@ -86,8 +83,8 @@ class LoadVDBtoVRay(load.LoaderPlugin):
def load(self, context, name, namespace, data):
- from ayon_core.hosts.maya.api.lib import unique_namespace
- from ayon_core.hosts.maya.api.pipeline import containerise
+ from ayon_maya.api.lib import unique_namespace
+ from ayon_maya.api.pipeline import containerise
path = self.filepath_from_context(context)
assert os.path.exists(path), (
@@ -159,7 +156,7 @@ class LoadVDBtoVRay(load.LoaderPlugin):
def _set_path(self, grid_node, path, show_preset_popup=True):
- from ayon_core.hosts.maya.api.lib import attribute_values
+ from ayon_maya.api.lib import attribute_values
from maya import cmds
def _get_filename_from_folder(path):
@@ -175,6 +172,7 @@ class LoadVDBtoVRay(load.LoaderPlugin):
else:
# Sequence
import clique
+
# todo: check support for negative frames as input
collections, remainder = clique.assemble(files)
assert len(collections) == 1, (
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py b/server_addon/maya/client/ayon_maya/plugins/load/load_vrayproxy.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_vrayproxy.py
index 14d645021c..c71a48247c 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_vrayproxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_vrayproxy.py
@@ -7,24 +7,17 @@ loader will use them instead of native vray vrmesh format.
"""
import os
-import maya.cmds as cmds
-
import ayon_api
+import maya.cmds as cmds
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path,
-)
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection,
- namespaced,
- unique_namespace
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api.lib import maintained_selection, namespaced, unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
+from ayon_maya.api.plugin import get_load_color_for_product_type
-class VRayProxyLoader(load.LoaderPlugin):
+class VRayProxyLoader(plugin.Loader):
"""Load VRay Proxy with Alembic or VrayMesh."""
product_types = {"vrayproxy", "model", "pointcache", "animation"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py b/server_addon/maya/client/ayon_maya/plugins/load/load_vrayscene.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_vrayscene.py
index ea3215da97..255ca844ba 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_vrayscene.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_vrayscene.py
@@ -1,20 +1,14 @@
# -*- coding: utf-8 -*-
import maya.cmds as cmds # noqa
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection,
- namespaced,
- unique_namespace
-)
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api.lib import maintained_selection, namespaced, unique_namespace
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
+from ayon_maya.api.plugin import get_load_color_for_product_type
-class VRaySceneLoader(load.LoaderPlugin):
+class VRaySceneLoader(plugin.Loader):
"""Load Vray scene"""
product_types = {"vrayscene_layer"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_xgen.py b/server_addon/maya/client/ayon_maya/plugins/load/load_xgen.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/load/load_xgen.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_xgen.py
index e2664439b0..88d9d550da 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_xgen.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_xgen.py
@@ -1,23 +1,21 @@
import os
import shutil
+from ayon_maya.api import plugin
import maya.cmds as cmds
import xgenm
-
+from ayon_core.pipeline import get_representation_path
+from ayon_maya.api import current_file
+from ayon_maya.api.lib import (
+ attribute_values,
+ get_container_members,
+ maintained_selection,
+ write_xgen_file,
+)
from qtpy import QtWidgets
-import ayon_core.hosts.maya.api.plugin
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection,
- get_container_members,
- attribute_values,
- write_xgen_file
-)
-from ayon_core.hosts.maya.api import current_file
-from ayon_core.pipeline import get_representation_path
-
-class XgenLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
+class XgenLoader(plugin.ReferenceLoader):
"""Load Xgen as reference"""
product_types = {"xgen"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py b/server_addon/maya/client/ayon_maya/plugins/load/load_yeti_cache.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_yeti_cache.py
index 4ca9ae9d03..6000de2507 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_cache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_yeti_cache.py
@@ -1,21 +1,17 @@
-import os
import json
+import os
import re
from collections import defaultdict
import clique
-from maya import cmds
-
+from ayon_core.pipeline import get_representation_path
from ayon_core.settings import get_project_settings
-from ayon_core.pipeline import (
- load,
- get_representation_path
-)
-from ayon_core.hosts.maya.api import lib
-from ayon_core.hosts.maya.api.yeti import create_yeti_variable
-from ayon_core.hosts.maya.api.pipeline import containerise
-from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
-
+from ayon_maya.api import lib
+from ayon_maya.api.pipeline import containerise
+from ayon_maya.api import plugin
+from ayon_maya.api.plugin import get_load_color_for_product_type
+from ayon_maya.api.yeti import create_yeti_variable
+from maya import cmds
# Do not reset these values on update but only apply on first load
# to preserve any potential local overrides
@@ -45,7 +41,7 @@ def set_attribute(node, attr, value):
lib.set_attribute(node, attr, value)
-class YetiCacheLoader(load.LoaderPlugin):
+class YetiCacheLoader(plugin.Loader):
"""Load Yeti Cache with one or more Yeti nodes"""
product_types = {"yeticache", "yetiRig"}
diff --git a/client/ayon_core/hosts/maya/plugins/load/load_yeti_rig.py b/server_addon/maya/client/ayon_maya/plugins/load/load_yeti_rig.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/load/load_yeti_rig.py
rename to server_addon/maya/client/ayon_maya/plugins/load/load_yeti_rig.py
index 7444566ee1..a45e5c63ef 100644
--- a/client/ayon_core/hosts/maya/plugins/load/load_yeti_rig.py
+++ b/server_addon/maya/client/ayon_maya/plugins/load/load_yeti_rig.py
@@ -1,12 +1,9 @@
from typing import List
import maya.cmds as cmds
-
-from ayon_core.hosts.maya.api import plugin
-from ayon_core.hosts.maya.api import lib
-
from ayon_core.pipeline import registered_host
from ayon_core.pipeline.create import CreateContext
+from ayon_maya.api import lib, plugin
class YetiRigLoader(plugin.ReferenceLoader):
diff --git a/client/ayon_core/hosts/maya/plugins/__init__.py b/server_addon/maya/client/ayon_maya/plugins/publish/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/__init__.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/__init__.py
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_animation.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_animation.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_animation.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_animation.py
index 391c80c84e..528d981c4b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_animation.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_animation.py
@@ -1,9 +1,9 @@
-import pyblish.api
-
import maya.cmds as cmds
+import pyblish.api
+from ayon_maya.api import plugin
-class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin):
+class CollectAnimationOutputGeometry(plugin.MayaInstancePlugin):
"""Collect out hierarchy data for instance.
Collect all hierarchy nodes which reside in the out_SET of the animation
@@ -18,7 +18,6 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.4
families = ["animation"]
label = "Collect Animation Output Geometry"
- hosts = ["maya"]
ignore_type = ["constraints"]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_arnold_scene_source.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_arnold_scene_source.py
similarity index 55%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_arnold_scene_source.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_arnold_scene_source.py
index 0db89bee31..c9dd0b8063 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_arnold_scene_source.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_arnold_scene_source.py
@@ -1,30 +1,32 @@
+import pyblish.api
+from ayon_maya.api.lib import get_all_children
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.hosts.maya.api.lib import get_all_children
-
-class CollectArnoldSceneSource(pyblish.api.InstancePlugin):
+class CollectArnoldSceneSource(plugin.MayaInstancePlugin):
"""Collect Arnold Scene Source data."""
# Offset to be after renderable camera collection.
order = pyblish.api.CollectorOrder + 0.2
label = "Collect Arnold Scene Source"
- families = ["ass"]
+ families = ["ass", "assProxy"]
def process(self, instance):
- objsets = instance.data["setMembers"]
+ instance.data["members"] = []
+ for set_member in instance.data["setMembers"]:
+ if cmds.nodeType(set_member) != "objectSet":
+ instance.data["members"].extend(self.get_hierarchy(set_member))
+ continue
- for objset in objsets:
- objset = str(objset)
- members = cmds.sets(objset, query=True)
+ members = cmds.sets(set_member, query=True)
members = cmds.ls(members, long=True)
if members is None:
- self.log.warning("Skipped empty instance: \"%s\" " % objset)
+ self.log.warning(
+ "Skipped empty instance: \"%s\" " % set_member
+ )
continue
- if objset.endswith("content_SET"):
- instance.data["contentMembers"] = self.get_hierarchy(members)
- if objset.endswith("proxy_SET"):
+ if set_member.endswith("proxy_SET"):
instance.data["proxy"] = self.get_hierarchy(members)
# Use camera in object set if present else default to render globals
@@ -33,7 +35,7 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin):
renderable = [c for c in cameras if cmds.getAttr("%s.renderable" % c)]
if renderable:
camera = renderable[0]
- for node in instance.data["contentMembers"]:
+ for node in instance.data["members"]:
camera_shapes = cmds.listRelatives(
node, shapes=True, type="camera"
)
@@ -46,18 +48,11 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin):
self.log.debug("data: {}".format(instance.data))
def get_hierarchy(self, nodes):
- """Return nodes with all their children.
-
- Arguments:
- nodes (List[str]): List of nodes to collect children hierarchy for
-
- Returns:
- list: Input nodes with their children hierarchy
-
- """
+ """Return nodes with all their children"""
nodes = cmds.ls(nodes, long=True)
if not nodes:
return []
-
- children = get_all_children(nodes, ignore_intermediate_objects=True)
- return list(children.union(nodes))
+ children = get_all_children(nodes)
+ # Make sure nodes merged with children only
+ # contains unique entries
+ return list(set(nodes + list(children)))
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_assembly.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_assembly.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_assembly.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_assembly.py
index eebbbd4447..e57d70662c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_assembly.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_assembly.py
@@ -1,14 +1,20 @@
+"""Collect all relevant assembly items.
+
+Todo:
+ Publish of assembly need unique namespace for all assets, we should
+ create validator for this.
+
+"""
from collections import defaultdict
import pyblish.api
from maya import cmds, mel
-from ayon_core.hosts.maya import api
-from ayon_core.hosts.maya.api import lib
-
-# TODO : Publish of assembly: -unique namespace for all assets, VALIDATOR!
+from ayon_maya import api
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class CollectAssembly(pyblish.api.InstancePlugin):
+class CollectAssembly(plugin.MayaInstancePlugin):
"""Collect all relevant assembly items
Collected data:
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_current_file.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_current_file.py
similarity index 78%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_current_file.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_current_file.py
index c7105a7f3c..8bd1908c73 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_current_file.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_current_file.py
@@ -1,15 +1,13 @@
-
import pyblish.api
-
+from ayon_maya.api import plugin
from maya import cmds
-class CollectCurrentFile(pyblish.api.ContextPlugin):
+class CollectCurrentFile(plugin.MayaContextPlugin):
"""Inject the current working file."""
order = pyblish.api.CollectorOrder - 0.4
label = "Maya Current File"
- hosts = ['maya']
def process(self, context):
"""Inject the current working file"""
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_fbx_animation.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_animation.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_fbx_animation.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_animation.py
index d8fd7a16e8..83f42667a5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_fbx_animation.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_animation.py
@@ -1,16 +1,16 @@
# -*- coding: utf-8 -*-
-from maya import cmds # noqa
import pyblish.api
from ayon_core.pipeline import OptionalPyblishPluginMixin
+from ayon_maya.api import plugin
+from maya import cmds # noqa
-class CollectFbxAnimation(pyblish.api.InstancePlugin,
+class CollectFbxAnimation(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Collect Animated Rig Data for FBX Extractor."""
order = pyblish.api.CollectorOrder + 0.2
label = "Collect Fbx Animation"
- hosts = ["maya"]
families = ["animation"]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_fbx_camera.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_camera.py
similarity index 85%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_fbx_camera.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_camera.py
index bfa5bccbb9..f6791b6e72 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_fbx_camera.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_camera.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
-from maya import cmds # noqa
import pyblish.api
+from ayon_maya.api import plugin
+from maya import cmds # noqa
-class CollectFbxCamera(pyblish.api.InstancePlugin):
+class CollectFbxCamera(plugin.MayaInstancePlugin):
"""Collect Camera for FBX export."""
order = pyblish.api.CollectorOrder + 0.2
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_file_dependencies.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_file_dependencies.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_file_dependencies.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_file_dependencies.py
index 60853bd1ee..74b8054c33 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_file_dependencies.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_file_dependencies.py
@@ -1,14 +1,13 @@
from maya import cmds
-
+from ayon_maya.api import plugin
import pyblish.api
-class CollectFileDependencies(pyblish.api.ContextPlugin):
+class CollectFileDependencies(plugin.MayaContextPlugin):
"""Gather all files referenced in this scene."""
label = "Collect File Dependencies"
order = pyblish.api.CollectorOrder - 0.49
- hosts = ["maya"]
families = ["renderlayer"]
@classmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_gltf.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_gltf.py
similarity index 85%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_gltf.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_gltf.py
index bb37fe3a7e..7ee23d289c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_gltf.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_gltf.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
import pyblish.api
+from ayon_maya.api import plugin
-class CollectGLTF(pyblish.api.InstancePlugin):
+class CollectGLTF(plugin.MayaInstancePlugin):
"""Collect Assets for GLTF/GLB export."""
order = pyblish.api.CollectorOrder + 0.2
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_history.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_history.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_history.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_history.py
index 2da74991c0..9041d4d1d5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_history.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_history.py
@@ -1,9 +1,9 @@
+import pyblish.api
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-
-class CollectMayaHistory(pyblish.api.InstancePlugin):
+class CollectMayaHistory(plugin.MayaInstancePlugin):
"""Collect history for instances from the Maya scene
Note:
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_inputs.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_inputs.py
index fa5a694a76..67d4a3f378 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_inputs.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_inputs.py
@@ -5,8 +5,9 @@ import maya.api.OpenMaya as om
import pyblish.api
from ayon_core.pipeline import registered_host
-from ayon_core.hosts.maya.api.lib import get_container_members
-from ayon_core.hosts.maya.api.lib_rendersetup import get_shader_in_layer
+from ayon_maya.api.lib import get_container_members
+from ayon_maya.api.lib_rendersetup import get_shader_in_layer
+from ayon_maya.api import plugin
def iter_history(nodes,
@@ -93,7 +94,7 @@ def collect_input_containers(containers, nodes):
if any(node in container["_members"] for node in nodes)]
-class CollectUpstreamInputs(pyblish.api.InstancePlugin):
+class CollectUpstreamInputs(plugin.MayaInstancePlugin):
"""Collect input source inputs for this publish.
This will include `inputs` data of which loaded publishes were used in the
@@ -104,7 +105,6 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin):
label = "Collect Inputs"
order = pyblish.api.CollectorOrder + 0.34
- hosts = ["maya"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_instances.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_instances.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_instances.py
index 774c217cfd..0ca43d4be9 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_instances.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_instances.py
@@ -1,10 +1,11 @@
from maya import cmds
import pyblish.api
-from ayon_core.hosts.maya.api.lib import get_all_children
+from ayon_maya.api.lib import get_all_children
+from ayon_maya.api import plugin
-class CollectNewInstances(pyblish.api.InstancePlugin):
+class CollectNewInstances(plugin.MayaInstancePlugin):
"""Gather members for instances and pre-defined attribute
This collector takes into account assets that are associated with
@@ -105,8 +106,10 @@ class CollectNewInstances(pyblish.api.InstancePlugin):
parents = set()
for node in nodes:
- splitted = node.split("|")
- items = ["|".join(splitted[0:i]) for i in range(2, len(splitted))]
+ split_parts = node.split("|")
+ items = [
+ "|".join(split_parts[:i]) for i in range(2, len(split_parts))
+ ]
parents.update(items)
return parents
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_look.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_look.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_look.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_look.py
index a3a32bc0cb..691933babd 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_look.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_look.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
"""Maya look collector."""
-import re
-import os
import glob
+import os
+import re
-from maya import cmds # noqa
import pyblish.api
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds # noqa
SHAPE_ATTRS = {"castsShadows",
"receiveShadows",
@@ -265,7 +266,7 @@ def get_file_node_files(node):
return result
-class CollectLook(pyblish.api.InstancePlugin):
+class CollectLook(plugin.MayaInstancePlugin):
"""Collect look data for instance.
For the shapes/transforms of the referenced object to collect look for
@@ -286,7 +287,6 @@ class CollectLook(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.2
families = ["look"]
label = "Collect Look"
- hosts = ["maya"]
def process(self, instance):
"""Collect the Look in the instance with the correct layer settings"""
@@ -644,7 +644,6 @@ class CollectModelRenderSets(CollectLook):
order = pyblish.api.CollectorOrder + 0.21
families = ["model"]
label = "Collect Model Render Sets"
- hosts = ["maya"]
def collect_sets(self, instance):
"""Collect all related objectSets except shadingEngines
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_maya_scene_time.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_scene_time.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_maya_scene_time.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_scene_time.py
index 6a20cb151f..c10d0fffbe 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_maya_scene_time.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_scene_time.py
@@ -1,9 +1,9 @@
+import pyblish.api
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-
-class CollectMayaSceneTime(pyblish.api.InstancePlugin):
+class CollectMayaSceneTime(plugin.MayaInstancePlugin):
"""Collect Maya Scene playback range
This allows to reproduce the playback range for the content to be loaded.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_maya_units.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_units.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_maya_units.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_units.py
index 2421641d26..47888506ff 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_maya_units.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_units.py
@@ -1,15 +1,16 @@
import maya.cmds as cmds
import maya.mel as mel
+from ayon_maya.api import plugin
+
import pyblish.api
-class CollectMayaUnits(pyblish.api.ContextPlugin):
+class CollectMayaUnits(plugin.MayaContextPlugin):
"""Collect Maya's scene units."""
label = "Maya Units"
order = pyblish.api.CollectorOrder
- hosts = ["maya"]
def process(self, context):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_maya_workspace.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_workspace.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_maya_workspace.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_workspace.py
index 122fabe8a1..a7b51e1fb3 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_maya_workspace.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_maya_workspace.py
@@ -1,17 +1,16 @@
import os
import pyblish.api
-
+from ayon_maya.api import plugin
from maya import cmds
-class CollectMayaWorkspace(pyblish.api.ContextPlugin):
+class CollectMayaWorkspace(plugin.MayaContextPlugin):
"""Inject the current workspace into context"""
order = pyblish.api.CollectorOrder - 0.5
label = "Maya Workspace"
- hosts = ['maya']
def process(self, context):
workspace = cmds.workspace(rootDirectory=True, query=True)
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_model.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_model.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_model.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_model.py
index 9d45ed63bc..13e5a609e7 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_model.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_model.py
@@ -1,9 +1,9 @@
+import pyblish.api
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-
-class CollectModelData(pyblish.api.InstancePlugin):
+class CollectModelData(plugin.MayaInstancePlugin):
"""Collect model data
Ensures always only a single frame is extracted (current frame).
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_multiverse_look.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_multiverse_look.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_multiverse_look.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_multiverse_look.py
index 83e743c92e..ddf36b7eda 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_multiverse_look.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_multiverse_look.py
@@ -2,9 +2,10 @@ import glob
import os
import re
-from maya import cmds
import pyblish.api
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
SHAPE_ATTRS = ["castsShadows",
"receiveShadows",
@@ -251,7 +252,7 @@ def is_mipmap(fname):
return False
-class CollectMultiverseLookData(pyblish.api.InstancePlugin):
+class CollectMultiverseLookData(plugin.MayaInstancePlugin):
"""Collect Multiverse Look
Searches through the overrides finding all material overrides. From there
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_pointcache.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_pointcache.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_pointcache.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_pointcache.py
index 5578a57f31..8d0b45137f 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_pointcache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_pointcache.py
@@ -1,15 +1,14 @@
+import pyblish.api
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-
-class CollectPointcache(pyblish.api.InstancePlugin):
+class CollectPointcache(plugin.MayaInstancePlugin):
"""Collect pointcache data for instance."""
order = pyblish.api.CollectorOrder + 0.4
families = ["pointcache"]
label = "Collect Pointcache"
- hosts = ["maya"]
def process(self, instance):
if instance.data.get("farm"):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_remove_marked.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_remove_marked.py
similarity index 86%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_remove_marked.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_remove_marked.py
index 69e69f6630..14d914cac5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_remove_marked.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_remove_marked.py
@@ -1,7 +1,8 @@
import pyblish.api
+from ayon_maya.api import plugin
-class CollectRemoveMarked(pyblish.api.ContextPlugin):
+class CollectRemoveMarked(plugin.MayaContextPlugin):
"""Remove marked data
Remove instances that have 'remove' in their instance.data
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_render.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_render.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_render.py
index 21095935a2..160a019540 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_render.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_render.py
@@ -32,24 +32,23 @@ Provides:
instance -> pixelAspect
"""
+import json
import os
import platform
-import json
-
-from maya import cmds
import pyblish.api
-
-from ayon_core.pipeline import KnownPublishError
from ayon_core.lib import get_formatted_current_time
-from ayon_core.hosts.maya.api.lib_renderproducts import (
+from ayon_core.pipeline import KnownPublishError
+from ayon_maya.api import lib
+from ayon_maya.api.lib_renderproducts import (
+ UnsupportedRendererException,
get as get_layer_render_products,
- UnsupportedRendererException
)
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
-class CollectMayaRender(pyblish.api.InstancePlugin):
+class CollectMayaRender(plugin.MayaInstancePlugin):
"""Gather all publishable render layers from renderSetup."""
order = pyblish.api.CollectorOrder + 0.01
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_render_layer_aovs.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_render_layer_aovs.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_render_layer_aovs.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_render_layer_aovs.py
index 1c83918155..dd4a8fefe5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_render_layer_aovs.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_render_layer_aovs.py
@@ -1,11 +1,10 @@
+import pyblish.api
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.hosts.maya.api import lib
-
-
-class CollectRenderLayerAOVS(pyblish.api.InstancePlugin):
+class CollectRenderLayerAOVS(plugin.MayaInstancePlugin):
"""Collect all render layer's AOVs / Render Elements that will render.
This collector is important to be able to Extend Frames.
@@ -26,7 +25,6 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.02
label = "Render Elements / AOVs"
- hosts = ["maya"]
families = ["renderlayer"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_renderable_camera.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_renderable_camera.py
similarity index 85%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_renderable_camera.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_renderable_camera.py
index 97d857079b..fbd181de3e 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_renderable_camera.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_renderable_camera.py
@@ -1,11 +1,10 @@
import pyblish.api
-
+from ayon_maya.api.lib_rendersetup import get_attr_in_layer
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.hosts.maya.api.lib_rendersetup import get_attr_in_layer
-
-class CollectRenderableCamera(pyblish.api.InstancePlugin):
+class CollectRenderableCamera(plugin.MayaInstancePlugin):
"""Collect the renderable camera(s) for the render layer"""
# Offset to be after renderlayer collection.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_review.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_review.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_review.py
index 4e35b3bcc2..8a50c2f0a9 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_review.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_review.py
@@ -1,13 +1,12 @@
-from maya import cmds, mel
-
import ayon_api
import pyblish.api
-
from ayon_core.pipeline import KnownPublishError
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds, mel
-class CollectReview(pyblish.api.InstancePlugin):
+class CollectReview(plugin.MayaInstancePlugin):
"""Collect Review data
"""
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_rig_sets.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_rig_sets.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_rig_sets.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_rig_sets.py
index 34ff26a8b8..98f4d38ab2 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_rig_sets.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_rig_sets.py
@@ -1,8 +1,9 @@
import pyblish.api
+from ayon_maya.api import plugin
from maya import cmds
-class CollectRigSets(pyblish.api.InstancePlugin):
+class CollectRigSets(plugin.MayaInstancePlugin):
"""Ensure rig contains pipeline-critical content
Every rig must contain at least two object sets:
@@ -13,7 +14,6 @@ class CollectRigSets(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.05
label = "Collect Rig Sets"
- hosts = ["maya"]
families = ["rig"]
accepted_output = ["mesh", "transform"]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_skeleton_mesh.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_skeleton_mesh.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_skeleton_mesh.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_skeleton_mesh.py
index 31f0eca88c..aaec4cb6d9 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_skeleton_mesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_skeleton_mesh.py
@@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
-from maya import cmds # noqa
import pyblish.api
+from ayon_maya.api import plugin
+from maya import cmds # noqa
-class CollectSkeletonMesh(pyblish.api.InstancePlugin):
+class CollectSkeletonMesh(plugin.MayaInstancePlugin):
"""Collect Static Rig Data for FBX Extractor."""
order = pyblish.api.CollectorOrder + 0.2
label = "Collect Skeleton Mesh"
- hosts = ["maya"]
families = ["rig"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_unreal_skeletalmesh.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_unreal_skeletalmesh.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_unreal_skeletalmesh.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_unreal_skeletalmesh.py
index 79693bb35e..32515a5957 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_unreal_skeletalmesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_unreal_skeletalmesh.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from maya import cmds # noqa
import pyblish.api
+from ayon_maya.api import plugin
-class CollectUnrealSkeletalMesh(pyblish.api.InstancePlugin):
+class CollectUnrealSkeletalMesh(plugin.MayaInstancePlugin):
"""Collect Unreal Skeletal Mesh."""
order = pyblish.api.CollectorOrder + 0.2
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_unreal_staticmesh.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_unreal_staticmesh.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_unreal_staticmesh.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_unreal_staticmesh.py
index 03b6c4a188..35295d6e3b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_unreal_staticmesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_unreal_staticmesh.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
from maya import cmds # noqa
import pyblish.api
+from ayon_maya.api import plugin
from pprint import pformat
-class CollectUnrealStaticMesh(pyblish.api.InstancePlugin):
+class CollectUnrealStaticMesh(plugin.MayaInstancePlugin):
"""Collect Unreal Static Mesh."""
order = pyblish.api.CollectorOrder + 0.2
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_user_defined_attributes.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_user_defined_attributes.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_user_defined_attributes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_user_defined_attributes.py
index 3d586d48fb..e468636def 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_user_defined_attributes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_user_defined_attributes.py
@@ -1,15 +1,14 @@
from maya import cmds
-
+from ayon_maya.api import plugin
import pyblish.api
-class CollectUserDefinedAttributes(pyblish.api.InstancePlugin):
+class CollectUserDefinedAttributes(plugin.MayaInstancePlugin):
"""Collect user defined attributes for nodes in instance."""
order = pyblish.api.CollectorOrder + 0.45
families = ["pointcache", "animation", "usd"]
label = "Collect User Defined Attributes"
- hosts = ["maya"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayproxy.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_vrayproxy.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_vrayproxy.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_vrayproxy.py
index 8630f56e58..a5491e5f9b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayproxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_vrayproxy.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
"""Collect Vray Proxy."""
import pyblish.api
+from ayon_maya.api import plugin
-class CollectVrayProxy(pyblish.api.InstancePlugin):
+class CollectVrayProxy(plugin.MayaInstancePlugin):
"""Collect Vray Proxy instance.
Add `pointcache` family for it.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_vrayscene.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_vrayscene.py
index 9548cd9387..f14735574e 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_vrayscene.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_vrayscene.py
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
"""Collect Vray Scene and prepare it for extraction and publishing."""
-
import pyblish.api
from ayon_core.lib import get_formatted_current_time
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class CollectVrayScene(pyblish.api.InstancePlugin):
+class CollectVrayScene(plugin.MayaInstancePlugin):
"""Collect Vray Scene.
If export on farm is checked, job is created to export it.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_workfile.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_workfile.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_workfile.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_workfile.py
index e2b64f1ebd..fa0689849a 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_workfile.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_workfile.py
@@ -1,13 +1,13 @@
import os
import pyblish.api
+from ayon_maya.api import plugin
-class CollectWorkfileData(pyblish.api.InstancePlugin):
+class CollectWorkfileData(plugin.MayaInstancePlugin):
"""Inject data into Workfile instance"""
order = pyblish.api.CollectorOrder - 0.01
label = "Maya Workfile"
- hosts = ['maya']
families = ["workfile"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_workscene_fps.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_workscene_fps.py
similarity index 78%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_workscene_fps.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_workscene_fps.py
index 41d6ffea33..a87483e67f 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_workscene_fps.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_workscene_fps.py
@@ -1,13 +1,13 @@
import pyblish.api
+from ayon_maya.api import plugin
from maya import mel
-class CollectWorksceneFPS(pyblish.api.ContextPlugin):
+class CollectWorksceneFPS(plugin.MayaContextPlugin):
"""Get the FPS of the work scene"""
label = "Workscene FPS"
order = pyblish.api.CollectorOrder
- hosts = ["maya"]
def process(self, context):
fps = mel.eval('currentTimeUnitToFPS()')
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_xgen.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_xgen.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_xgen.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_xgen.py
index f8f506376d..9ef17f3399 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_xgen.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_xgen.py
@@ -1,12 +1,12 @@
import os
+import pyblish.api
+from ayon_maya.api.lib import get_attribute_input
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.hosts.maya.api.lib import get_attribute_input
-
-class CollectXgen(pyblish.api.InstancePlugin):
+class CollectXgen(plugin.MayaInstancePlugin):
"""Collect Xgen"""
order = pyblish.api.CollectorOrder + 0.499999
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_yeti_cache.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_yeti_cache.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_yeti_cache.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_yeti_cache.py
index e1755e4212..44de461126 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_yeti_cache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_yeti_cache.py
@@ -1,10 +1,8 @@
-from maya import cmds
-
import pyblish.api
-
-from ayon_core.hosts.maya.api import lib
-from ayon_core.hosts.maya.api.yeti import get_yeti_user_variables
-
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from ayon_maya.api.yeti import get_yeti_user_variables
+from maya import cmds
SETTINGS = {
# Preview
@@ -24,7 +22,7 @@ SETTINGS = {
}
-class CollectYetiCache(pyblish.api.InstancePlugin):
+class CollectYetiCache(plugin.MayaInstancePlugin):
"""Collect all information of the Yeti caches
The information contains the following attributes per Yeti node
@@ -41,7 +39,6 @@ class CollectYetiCache(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.45
label = "Collect Yeti Cache"
families = ["yetiRig", "yeticache", "yeticacheUE"]
- hosts = ["maya"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/collect_yeti_rig.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_yeti_rig.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/collect_yeti_rig.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/collect_yeti_rig.py
index 8964e17f14..dbdc10789f 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/collect_yeti_rig.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_yeti_rig.py
@@ -1,13 +1,11 @@
import os
import re
-from maya import cmds
-
import pyblish.api
-
-from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline.publish import KnownPublishError
-
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
SETTINGS = {"renderDensity",
"renderWidth",
@@ -17,13 +15,12 @@ SETTINGS = {"renderDensity",
"cbId"}
-class CollectYetiRig(pyblish.api.InstancePlugin):
+class CollectYetiRig(plugin.MayaInstancePlugin):
"""Collect all information of the Yeti Rig"""
order = pyblish.api.CollectorOrder + 0.4
label = "Collect Yeti Rig"
families = ["yetiRig"]
- hosts = ["maya"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py b/server_addon/maya/client/ayon_maya/plugins/publish/determine_future_version.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/determine_future_version.py
index 5b597f2707..0c05b499c0 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/determine_future_version.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/determine_future_version.py
@@ -1,13 +1,13 @@
import pyblish.api
+from ayon_maya.api import plugin
-class DetermineFutureVersion(pyblish.api.InstancePlugin):
+class DetermineFutureVersion(plugin.MayaInstancePlugin):
"""
This will determine version of product if we want render to be attached to.
"""
label = "Determine Product Version"
order = pyblish.api.IntegratorOrder
- hosts = ["maya"]
families = ["renderlayer"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_active_view_thumbnail.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_active_view_thumbnail.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_active_view_thumbnail.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_active_view_thumbnail.py
index b5054b4846..290f7e24eb 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_active_view_thumbnail.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_active_view_thumbnail.py
@@ -1,13 +1,13 @@
-import maya.api.OpenMaya as om
-import maya.api.OpenMayaUI as omui
-
-import pyblish.api
import tempfile
-from ayon_core.hosts.maya.api.lib import IS_HEADLESS
+import maya.api.OpenMaya as om
+import maya.api.OpenMayaUI as omui
+import pyblish.api
+from ayon_maya.api.lib import IS_HEADLESS
+from ayon_maya.api import plugin
-class ExtractActiveViewThumbnail(pyblish.api.InstancePlugin):
+class ExtractActiveViewThumbnail(plugin.MayaInstancePlugin):
"""Set instance thumbnail to a screengrab of current active viewport.
This makes it so that if an instance does not have a thumbnail set yet that
@@ -18,7 +18,6 @@ class ExtractActiveViewThumbnail(pyblish.api.InstancePlugin):
order = pyblish.api.ExtractorOrder + 0.49
label = "Active View Thumbnail"
families = ["workfile"]
- hosts = ["maya"]
def process(self, instance):
if IS_HEADLESS:
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_arnold_scene_source.py
similarity index 64%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_arnold_scene_source.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_arnold_scene_source.py
index ed8f2ad40c..b39c875400 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_arnold_scene_source.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_arnold_scene_source.py
@@ -1,24 +1,20 @@
+import json
import os
from collections import defaultdict
-import json
-from maya import cmds
import arnold
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib, plugin
+from maya import cmds
-class ExtractArnoldSceneSource(publish.Extractor):
+class ExtractArnoldSceneSource(plugin.MayaExtractorPlugin):
"""Extract the content of the instance to an Arnold Scene Source file."""
label = "Extract Arnold Scene Source"
- hosts = ["maya"]
families = ["ass"]
asciiAss = False
- def process(self, instance):
- staging_dir = self.staging_dir(instance)
+ def _pre_process(self, instance, staging_dir):
file_path = os.path.join(staging_dir, "{}.ass".format(instance.name))
# Mask
@@ -70,24 +66,38 @@ class ExtractArnoldSceneSource(publish.Extractor):
"mask": mask
}
- filenames, nodes_by_id = self._extract(
- instance.data["contentMembers"], attribute_data, kwargs
- )
-
if "representations" not in instance.data:
instance.data["representations"] = []
+ return attribute_data, kwargs
+
+ def process(self, instance):
+ staging_dir = self.staging_dir(instance)
+ attribute_data, kwargs = self._pre_process(instance, staging_dir)
+
+ filenames = self._extract(
+ instance.data["members"], attribute_data, kwargs
+ )
+
+ self._post_process(
+ instance, filenames, staging_dir, kwargs["startFrame"]
+ )
+
+ def _post_process(self, instance, filenames, staging_dir, frame_start):
+ nodes_by_id = self._nodes_by_id(instance[:])
representation = {
"name": "ass",
"ext": "ass",
"files": filenames if len(filenames) > 1 else filenames[0],
"stagingDir": staging_dir,
- "frameStart": kwargs["startFrame"]
+ "frameStart": frame_start
}
instance.data["representations"].append(representation)
- json_path = os.path.join(staging_dir, "{}.json".format(instance.name))
+ json_path = os.path.join(
+ staging_dir, "{}.json".format(instance.name)
+ )
with open(json_path, "w") as f:
json.dump(nodes_by_id, f)
@@ -104,13 +114,68 @@ class ExtractArnoldSceneSource(publish.Extractor):
"Extracted instance {} to: {}".format(instance.name, staging_dir)
)
- # Extract proxy.
- if not instance.data.get("proxy", []):
- return
+ def _nodes_by_id(self, nodes):
+ nodes_by_id = defaultdict(list)
- kwargs["filename"] = file_path.replace(".ass", "_proxy.ass")
+ for node in nodes:
+ id = lib.get_id(node)
- filenames, _ = self._extract(
+ if id is None:
+ continue
+
+ # Converting Maya hierarchy separator "|" to Arnold separator "/".
+ nodes_by_id[id].append(node.replace("|", "/"))
+
+ return nodes_by_id
+
+ def _extract(self, nodes, attribute_data, kwargs):
+ filenames = []
+ with lib.attribute_values(attribute_data):
+ with lib.maintained_selection():
+ self.log.debug(
+ "Writing: {}".format(nodes)
+ )
+ cmds.select(nodes, noExpand=True)
+
+ self.log.debug(
+ "Extracting ass sequence with: {}".format(kwargs)
+ )
+
+ exported_files = cmds.arnoldExportAss(**kwargs)
+
+ for file in exported_files:
+ filenames.append(os.path.split(file)[1])
+
+ self.log.debug("Exported: {}".format(filenames))
+
+ return filenames
+
+
+class ExtractArnoldSceneSourceProxy(ExtractArnoldSceneSource):
+ """Extract the content of the instance to an Arnold Scene Source file."""
+
+ label = "Extract Arnold Scene Source Proxy"
+ hosts = ["maya"]
+ families = ["assProxy"]
+ asciiAss = True
+
+ def process(self, instance):
+ staging_dir = self.staging_dir(instance)
+ attribute_data, kwargs = self._pre_process(instance, staging_dir)
+
+ filenames, _ = self._duplicate_extract(
+ instance.data["members"], attribute_data, kwargs
+ )
+
+ self._post_process(
+ instance, filenames, staging_dir, kwargs["startFrame"]
+ )
+
+ kwargs["filename"] = os.path.join(
+ staging_dir, "{}_proxy.ass".format(instance.name)
+ )
+
+ filenames, _ = self._duplicate_extract(
instance.data["proxy"], attribute_data, kwargs
)
@@ -125,12 +190,11 @@ class ExtractArnoldSceneSource(publish.Extractor):
instance.data["representations"].append(representation)
- def _extract(self, nodes, attribute_data, kwargs):
+ def _duplicate_extract(self, nodes, attribute_data, kwargs):
self.log.debug(
"Writing {} with:\n{}".format(kwargs["filename"], kwargs)
)
filenames = []
- nodes_by_id = defaultdict(list)
# Duplicating nodes so they are direct children of the world. This
# makes the hierarchy of any exported ass file the same.
with lib.delete_after() as delete_bin:
@@ -147,7 +211,9 @@ class ExtractArnoldSceneSource(publish.Extractor):
if not shapes:
continue
- duplicate_transform = cmds.duplicate(node)[0]
+ basename = cmds.duplicate(node)[0]
+ parents = cmds.ls(node, long=True)[0].split("|")[:-1]
+ duplicate_transform = "|".join(parents + [basename])
if cmds.listRelatives(duplicate_transform, parent=True):
duplicate_transform = cmds.parent(
@@ -172,28 +238,7 @@ class ExtractArnoldSceneSource(publish.Extractor):
duplicate_nodes.extend(shapes)
delete_bin.append(duplicate_transform)
- # Copy cbId to mtoa_constant.
- for node in duplicate_nodes:
- # Converting Maya hierarchy separator "|" to Arnold
- # separator "/".
- nodes_by_id[lib.get_id(node)].append(node.replace("|", "/"))
-
- with lib.attribute_values(attribute_data):
- with lib.maintained_selection():
- self.log.debug(
- "Writing: {}".format(duplicate_nodes)
- )
- cmds.select(duplicate_nodes, noExpand=True)
-
- self.log.debug(
- "Extracting ass sequence with: {}".format(kwargs)
- )
-
- exported_files = cmds.arnoldExportAss(**kwargs)
-
- for file in exported_files:
- filenames.append(os.path.split(file)[1])
-
- self.log.debug("Exported: {}".format(filenames))
+ nodes_by_id = self._nodes_by_id(duplicate_nodes)
+ filenames = self._extract(duplicate_nodes, attribute_data, kwargs)
return filenames, nodes_by_id
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_assembly.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_assembly.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_assembly.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_assembly.py
index 5f51dc38cb..8460fb716f 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_assembly.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_assembly.py
@@ -1,13 +1,12 @@
-import os
import json
+import os
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.alembic import extract_alembic
-
+from ayon_maya.api.alembic import extract_alembic
+from ayon_maya.api import plugin
from maya import cmds
-class ExtractAssembly(publish.Extractor):
+class ExtractAssembly(plugin.MayaExtractorPlugin):
"""Produce an alembic of just point positions and normals.
Positions and normals are preserved, but nothing more,
@@ -16,7 +15,6 @@ class ExtractAssembly(publish.Extractor):
"""
label = "Extract Assembly"
- hosts = ["maya"]
families = ["assembly"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_alembic.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_camera_alembic.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_camera_alembic.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_camera_alembic.py
index 74abc8de75..b5ce6a6a44 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_alembic.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_camera_alembic.py
@@ -2,12 +2,12 @@ import os
import json
from maya import cmds
-
+from ayon_maya.api import plugin
from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
-class ExtractCameraAlembic(publish.Extractor,
+class ExtractCameraAlembic(plugin.MayaExtractorPlugin,
publish.OptionalPyblishPluginMixin):
"""Extract a Camera as Alembic.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_mayaScene.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_camera_mayaScene.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_camera_mayaScene.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_camera_mayaScene.py
index cb3951ec0c..c5aa331cb2 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_camera_mayaScene.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_camera_mayaScene.py
@@ -1,16 +1,14 @@
# -*- coding: utf-8 -*-
"""Extract camera as Maya Scene."""
-import os
-import itertools
import contextlib
+import itertools
+import os
-from maya import cmds
-
+from ayon_core.lib import BoolDef
from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
-from ayon_core.lib import (
- BoolDef
-)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
def massage_ma_file(path):
@@ -82,7 +80,7 @@ def unlock(plug):
cmds.disconnectAttr(source, destination)
-class ExtractCameraMayaScene(publish.Extractor,
+class ExtractCameraMayaScene(plugin.MayaExtractorPlugin,
publish.OptionalPyblishPluginMixin):
"""Extract a Camera as Maya Scene.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_fbx.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_fbx.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_fbx.py
index bb2a6dad07..d9b0a789c5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_fbx.py
@@ -1,16 +1,15 @@
# -*- coding: utf-8 -*-
import os
-from maya import cmds # noqa
import maya.mel as mel # noqa
import pyblish.api
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
-from ayon_core.hosts.maya.api import fbx
+from ayon_maya.api import fbx
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
+from maya import cmds # noqa
-class ExtractFBX(publish.Extractor):
+class ExtractFBX(plugin.MayaExtractorPlugin):
"""Extract FBX from Maya.
This extracts reproducible FBX exports ignoring any of the
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_fbx_animation.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_fbx_animation.py
index ee66ed2fb7..c22241d2ca 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_fbx_animation.py
@@ -1,17 +1,14 @@
# -*- coding: utf-8 -*-
import os
-from maya import cmds # noqa
import pyblish.api
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import fbx
-from ayon_core.hosts.maya.api.lib import (
- namespaced, get_namespace, strip_namespace
-)
+from ayon_maya.api import fbx
+from ayon_maya.api.lib import get_namespace, namespaced, strip_namespace
+from ayon_maya.api import plugin
+from maya import cmds # noqa
-class ExtractFBXAnimation(publish.Extractor):
+class ExtractFBXAnimation(plugin.MayaExtractorPlugin):
"""Extract Rig in FBX format from Maya.
This extracts the rig in fbx with the constraints
@@ -22,7 +19,6 @@ class ExtractFBXAnimation(publish.Extractor):
"""
order = pyblish.api.ExtractorOrder
label = "Extract Animation (FBX)"
- hosts = ["maya"]
families = ["animation.fbx"]
def process(self, instance):
@@ -35,7 +31,8 @@ class ExtractFBXAnimation(publish.Extractor):
fbx_exporter = fbx.FBXExtractor(log=self.log)
out_members = instance.data.get("animated_skeleton", [])
# Export
- instance.data["constraints"] = True
+ # TODO: need to set up the options for users to set up
+ # the flags they intended to export
instance.data["skeletonDefinitions"] = True
instance.data["referencedAssetsContent"] = True
fbx_exporter.set_options_from_instance(instance)
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_gltf.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_gltf.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_gltf.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_gltf.py
index ff11bf0c1f..46da8f9463 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_gltf.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_gltf.py
@@ -1,17 +1,15 @@
import os
-from maya import cmds, mel
import pyblish.api
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
-from ayon_core.hosts.maya.api.gltf import extract_gltf
+from ayon_maya.api import lib
+from ayon_maya.api.gltf import extract_gltf
+from ayon_maya.api import plugin
+from maya import cmds, mel
-class ExtractGLB(publish.Extractor):
+class ExtractGLB(plugin.MayaExtractorPlugin):
order = pyblish.api.ExtractorOrder
- hosts = ["maya"]
label = "Extract GLB"
families = ["gltf"]
@@ -39,7 +37,7 @@ class ExtractGLB(publish.Extractor):
"aet": end_frame,
"afr": fps,
"dsa": 1,
- "acn": instance.name,
+ "acn": instance.name, # codespell:ignore acn
"glb": True,
"vno": True # visibleNodeOnly
}
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_gpu_cache.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_gpu_cache.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_gpu_cache.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_gpu_cache.py
index 4b293b5785..53944571a8 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_gpu_cache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_gpu_cache.py
@@ -1,16 +1,15 @@
import json
+from ayon_core.pipeline import publish
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-
-class ExtractGPUCache(publish.Extractor,
+class ExtractGPUCache(plugin.MayaExtractorPlugin,
publish.OptionalPyblishPluginMixin):
"""Extract the content of the instance to a GPU cache file."""
label = "GPU Cache"
- hosts = ["maya"]
families = ["model", "animation", "pointcache"]
step = 1.0
stepSave = 1
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_import_reference.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_import_reference.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_import_reference.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_import_reference.py
index 3fb84c8d83..b5964e426b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_import_reference.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_import_reference.py
@@ -1,18 +1,16 @@
import os
import sys
-
-from maya import cmds
-
-import pyblish.api
import tempfile
+import pyblish.api
from ayon_core.lib import run_subprocess
-from ayon_core.pipeline import publish
from ayon_core.pipeline.publish import OptionalPyblishPluginMixin
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
-class ExtractImportReference(publish.Extractor,
+class ExtractImportReference(plugin.MayaExtractorPlugin,
OptionalPyblishPluginMixin):
"""
@@ -24,7 +22,6 @@ class ExtractImportReference(publish.Extractor,
label = "Extract Import Reference"
order = pyblish.api.ExtractorOrder - 0.48
- hosts = ["maya"]
families = ["renderlayer", "workfile"]
optional = True
tmp_format = "_tmp"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_layout.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_layout.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_layout.py
index b025a1605a..ca53f563d4 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_layout.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_layout.py
@@ -1,19 +1,17 @@
+import json
import math
import os
-import json
+from ayon_api import get_representation_by_id
+from ayon_maya.api import plugin
from maya import cmds
from maya.api import OpenMaya as om
-from ayon_api import get_representation_by_id
-
-from ayon_core.pipeline import publish
-class ExtractLayout(publish.Extractor):
+class ExtractLayout(plugin.MayaExtractorPlugin):
"""Extract a layout."""
label = "Extract Layout"
- hosts = ["maya"]
families = ["layout"]
project_container = "AVALON_CONTAINERS"
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_look.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_look.py
similarity index 99%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_look.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_look.py
index 2a86b20131..8e57b22d64 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_look.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_look.py
@@ -1,31 +1,29 @@
# -*- coding: utf-8 -*-
"""Maya look extractor."""
-import os
-import sys
import contextlib
import json
import logging
-import tempfile
+import os
import platform
+import sys
+import tempfile
from abc import ABCMeta, abstractmethod
from collections import OrderedDict
-import six
import attr
import pyblish.api
-
-from maya import cmds # noqa
-
+import six
from ayon_core.lib import (
- find_executable,
- source_hash,
- run_subprocess,
- get_oiio_tool_args,
ToolNotFoundError,
+ find_executable,
+ get_oiio_tool_args,
+ run_subprocess,
+ source_hash,
)
-
-from ayon_core.pipeline import publish, KnownPublishError
-from ayon_core.hosts.maya.api import lib
+from ayon_core.pipeline import KnownPublishError
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds # noqa
# Modes for transfer
COPY = 1
@@ -407,7 +405,7 @@ class MakeTX(TextureProcessor):
return False
-class ExtractLook(publish.Extractor):
+class ExtractLook(plugin.MayaExtractorPlugin):
"""Extract Look (Maya Scene + JSON)
Only extracts the sets (shadingEngines and alike) alongside a .json file
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py
index 2fd4f44449..6e66353c7a 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_scene_raw.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py
@@ -2,26 +2,21 @@
"""Extract data as Maya scene (raw)."""
import os
+from ayon_core.lib import BoolDef
+from ayon_core.pipeline import AVALON_CONTAINER_ID, AYON_CONTAINER_ID
+from ayon_core.pipeline.publish import AYONPyblishPluginMixin
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.hosts.maya.api.lib import maintained_selection
-from ayon_core.pipeline import (
- AYON_CONTAINER_ID,
- AVALON_CONTAINER_ID,
- publish,
-)
-from ayon_core.pipeline.publish import AYONPyblishPluginMixin
-from ayon_core.lib import BoolDef
-
-class ExtractMayaSceneRaw(publish.Extractor, AYONPyblishPluginMixin):
+class ExtractMayaSceneRaw(plugin.MayaExtractorPlugin, AYONPyblishPluginMixin):
"""Extract as Maya Scene (raw).
This will preserve all references, construction history, etc.
"""
label = "Maya Scene (Raw)"
- hosts = ["maya"]
families = ["mayaAscii",
"mayaScene",
"setdress",
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_usd.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_usd.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_maya_usd.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_usd.py
index cfaea8e479..d2bf98afbc 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_maya_usd.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_usd.py
@@ -1,13 +1,12 @@
-import os
-import six
-import json
import contextlib
-
-from maya import cmds
+import json
+import os
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
+import six
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
+from maya import cmds
@contextlib.contextmanager
@@ -112,14 +111,13 @@ def usd_export_attributes(nodes, attrs=None, attr_prefixes=None, mapping=None):
dg_mod.undoIt()
-class ExtractMayaUsd(publish.Extractor):
+class ExtractMayaUsd(plugin.MayaExtractorPlugin):
"""Extractor for Maya USD Asset data.
Upon publish a .usd (or .usdz) asset file will typically be written.
"""
label = "Extract Maya USD Asset"
- hosts = ["maya"]
families = ["mayaUsd"]
@property
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_model.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_model.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_model.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_model.py
index 543af59e8f..7f257a2013 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_model.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_model.py
@@ -2,13 +2,13 @@
"""Extract model as Maya Scene."""
import os
+from ayon_core.pipeline import publish
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
-
-class ExtractModel(publish.Extractor,
+class ExtractModel(plugin.MayaExtractorPlugin,
publish.OptionalPyblishPluginMixin):
"""Extract as Model (Maya Scene).
@@ -25,7 +25,6 @@ class ExtractModel(publish.Extractor,
"""
label = "Model (Maya Scene)"
- hosts = ["maya"]
families = ["model"]
scene_type = "ma"
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_look.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_look.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_look.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_look.py
index 2dd8821b3a..b6f8043a93 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_look.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_look.py
@@ -2,11 +2,11 @@ import os
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
-class ExtractMultiverseLook(publish.Extractor):
+class ExtractMultiverseLook(plugin.MayaExtractorPlugin):
"""Extractor for Multiverse USD look data.
This will extract:
@@ -34,7 +34,6 @@ class ExtractMultiverseLook(publish.Extractor):
"""
label = "Extract Multiverse USD Look"
- hosts = ["maya"]
families = ["mvLook"]
scene_type = "usda"
file_formats = ["usda", "usd"]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd.py
index 8c195c25fd..477af9dc26 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd.py
@@ -1,15 +1,13 @@
import os
-import six
-
-from maya import cmds
-from maya import mel
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
+import six
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
+from maya import cmds, mel
-class ExtractMultiverseUsd(publish.Extractor):
+class ExtractMultiverseUsd(plugin.MayaExtractorPlugin):
"""Extractor for Multiverse USD Asset data.
This will extract settings for a Multiverse Write Asset operation:
@@ -27,7 +25,6 @@ class ExtractMultiverseUsd(publish.Extractor):
"""
label = "Extract Multiverse USD Asset"
- hosts = ["maya"]
families = ["mvUsd"]
scene_type = "usd"
file_formats = ["usd", "usda", "usdz"]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd_comp.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd_comp.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd_comp.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd_comp.py
index d31660d1b4..3d18bb80e1 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd_comp.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd_comp.py
@@ -1,12 +1,11 @@
import os
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
-
-class ExtractMultiverseUsdComposition(publish.Extractor):
+class ExtractMultiverseUsdComposition(plugin.MayaExtractorPlugin):
"""Extractor of Multiverse USD Composition data.
This will extract settings for a Multiverse Write Composition operation:
@@ -24,7 +23,6 @@ class ExtractMultiverseUsdComposition(publish.Extractor):
"""
label = "Extract Multiverse USD Composition"
- hosts = ["maya"]
families = ["mvUsdComposition"]
scene_type = "usd"
# Order of `fileFormat` must match create_multiverse_usd_comp.py
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd_over.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd_over.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd_over.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd_over.py
index 00303e604c..a67f5c0a99 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_multiverse_usd_over.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_multiverse_usd_over.py
@@ -1,12 +1,11 @@
import os
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
-
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
from maya import cmds
-class ExtractMultiverseUsdOverride(publish.Extractor):
+class ExtractMultiverseUsdOverride(plugin.MayaExtractorPlugin):
"""Extractor for Multiverse USD Override data.
This will extract settings for a Multiverse Write Override operation:
@@ -22,7 +21,6 @@ class ExtractMultiverseUsdOverride(publish.Extractor):
"""
label = "Extract Multiverse USD Override"
- hosts = ["maya"]
families = ["mvUsdOverride"]
scene_type = "usd"
# Order of `fileFormat` must match create_multiverse_usd_over.py
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_obj.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_obj.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_obj.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_obj.py
index 6ce40a8728..baf86b581e 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_obj.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_obj.py
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
import os
-from maya import cmds
import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
-class ExtractObj(publish.Extractor):
+class ExtractObj(plugin.MayaExtractorPlugin):
"""Extract OBJ from Maya.
This extracts reproducible OBJ exports ignoring any of the settings
@@ -15,7 +15,6 @@ class ExtractObj(publish.Extractor):
"""
order = pyblish.api.ExtractorOrder
- hosts = ["maya"]
label = "Extract OBJ"
families = ["model"]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_playblast.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_playblast.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_playblast.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_playblast.py
index a394d880ff..539246eef0 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_playblast.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_playblast.py
@@ -1,14 +1,12 @@
import os
import clique
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
-
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-class ExtractPlayblast(publish.Extractor):
+class ExtractPlayblast(plugin.MayaExtractorPlugin):
"""Extract viewport playblast.
Takes review camera and creates review Quicktime video based on viewport
@@ -17,7 +15,6 @@ class ExtractPlayblast(publish.Extractor):
"""
label = "Extract Playblast"
- hosts = ["maya"]
families = ["review"]
optional = True
capture_preset = {}
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_pointcache.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_pointcache.py
index d7f9594374..d3e9d89aaf 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_pointcache.py
@@ -1,28 +1,28 @@
import os
from collections import OrderedDict
-from maya import cmds
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.alembic import extract_alembic
-from ayon_core.hosts.maya.api.lib import (
- suspended_refresh,
- maintained_selection,
- iter_visible_nodes_in_range
-)
from ayon_core.lib import (
BoolDef,
- TextDef,
- NumberDef,
EnumDef,
- UISeparatorDef,
+ NumberDef,
+ TextDef,
UILabelDef,
+ UISeparatorDef,
)
-from ayon_core.pipeline.publish import AYONPyblishPluginMixin
from ayon_core.pipeline import KnownPublishError
+from ayon_core.pipeline.publish import AYONPyblishPluginMixin
+from ayon_maya.api.alembic import extract_alembic
+from ayon_maya.api.lib import (
+ get_all_children,
+ iter_visible_nodes_in_range,
+ maintained_selection,
+ suspended_refresh,
+)
+from ayon_maya.api import plugin
+from maya import cmds
-class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
+class ExtractAlembic(plugin.MayaExtractorPlugin, AYONPyblishPluginMixin):
"""Produce an alembic of just point positions and normals.
Positions and normals, uvs, creases are preserved, but nothing more,
@@ -40,7 +40,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
# From settings
attr = []
attrPrefix = []
- autoSubd = False
bake_attributes = []
bake_attribute_prefixes = []
dataFormat = "ogawa"
@@ -63,6 +62,7 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
wholeFrameGeo = False
worldSpace = True
writeColorSets = False
+ writeCreases = False
writeFaceSets = False
writeNormals = True
writeUVSets = False
@@ -173,15 +173,9 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
"writeVisibility": attribute_values.get(
"writeVisibility", self.writeVisibility
),
- "autoSubd": attribute_values.get(
- "autoSubd", self.autoSubd
- ),
"uvsOnly": attribute_values.get(
"uvsOnly", self.uvsOnly
),
- "writeNormals": attribute_values.get(
- "writeNormals", self.writeNormals
- ),
"melPerFrameCallback": attribute_values.get(
"melPerFrameCallback", self.melPerFrameCallback
),
@@ -193,7 +187,12 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
),
"pythonPostJobCallback": attribute_values.get(
"pythonPostJobCallback", self.pythonPostJobCallback
- )
+ ),
+ # Note that this converts `writeNormals` to `noNormals` for the
+ # `AbcExport` equivalent in `extract_alembic`
+ "noNormals": not attribute_values.get(
+ "writeNormals", self.writeNormals
+ ),
}
if instance.data.get("visibleOnly", False):
@@ -249,7 +248,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
with maintained_selection():
cmds.select(instance.data["proxy"])
extract_alembic(**kwargs)
-
representation = {
"name": "proxy",
"ext": "abc",
@@ -268,20 +266,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
return []
override_defs = OrderedDict({
- "autoSubd": BoolDef(
- "autoSubd",
- label="Auto Subd",
- default=cls.autoSubd,
- tooltip=(
- "If this flag is present and the mesh has crease edges, "
- "crease vertices or holes, the mesh (OPolyMesh) would now "
- "be written out as an OSubD and crease info will be stored"
- " in the Alembic file. Otherwise, creases info won't be "
- "preserved in Alembic file unless a custom Boolean "
- "attribute SubDivisionMesh has been added to mesh node and"
- " its value is true."
- )
- ),
"eulerFilter": BoolDef(
"eulerFilter",
label="Euler Filter",
@@ -354,6 +338,13 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin):
default=cls.writeColorSets,
tooltip="Write vertex colors with the geometry."
),
+ "writeCreases": BoolDef(
+ "writeCreases",
+ label="Write Creases",
+ default=cls.writeCreases,
+ tooltip="Write the geometry's edge and vertex crease "
+ "information."
+ ),
"writeFaceSets": BoolDef(
"writeFaceSets",
label="Write Face Sets",
@@ -527,9 +518,7 @@ class ExtractAnimation(ExtractAlembic):
roots = cmds.sets(out_set, query=True) or []
# Include all descendants
- nodes = roots
- nodes += cmds.listRelatives(
- roots, allDescendents=True, fullPath=True
- ) or []
+ nodes = roots.copy()
+ nodes.extend(get_all_children(roots, ignore_intermediate_objects=True))
return nodes, roots
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_proxy_abc.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_proxy_abc.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_proxy_abc.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_proxy_abc.py
index 5aefdfc33a..fc1c7981ed 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_proxy_abc.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_proxy_abc.py
@@ -1,22 +1,20 @@
import os
+from ayon_maya.api.alembic import extract_alembic
+from ayon_maya.api.lib import (
+ iter_visible_nodes_in_range,
+ maintained_selection,
+ suspended_refresh,
+)
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.alembic import extract_alembic
-from ayon_core.hosts.maya.api.lib import (
- suspended_refresh,
- maintained_selection,
- iter_visible_nodes_in_range
-)
-
-class ExtractProxyAlembic(publish.Extractor):
+class ExtractProxyAlembic(plugin.MayaExtractorPlugin):
"""Produce an alembic for bounding box geometry
"""
label = "Extract Proxy (Alembic)"
- hosts = ["maya"]
families = ["proxyAbc"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_redshift_proxy.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_redshift_proxy.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_redshift_proxy.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_redshift_proxy.py
index 66dd805437..909d3dd172 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_redshift_proxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_redshift_proxy.py
@@ -2,23 +2,18 @@
"""Redshift Proxy extractor."""
import os
+from ayon_maya.api.lib import maintained_selection, renderlayer
+from ayon_maya.api import plugin
+from ayon_maya.api.render_setup_tools import (
+ allow_export_from_render_setup_layer,
+)
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection,
- renderlayer
-)
-from ayon_core.hosts.maya.api.render_setup_tools import (
- allow_export_from_render_setup_layer
-)
-
-class ExtractRedshiftProxy(publish.Extractor):
+class ExtractRedshiftProxy(plugin.MayaExtractorPlugin):
"""Extract the content of the instance to a redshift proxy file."""
label = "Redshift Proxy (.rs)"
- hosts = ["maya"]
families = ["redshiftproxy"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_rendersetup.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_rendersetup.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_rendersetup.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_rendersetup.py
index 4815033777..8dcdd603b7 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_rendersetup.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_rendersetup.py
@@ -1,11 +1,11 @@
-import os
import json
+import os
import maya.app.renderSetup.model.renderSetup as renderSetup
-from ayon_core.pipeline import publish
+from ayon_maya.api import plugin
-class ExtractRenderSetup(publish.Extractor):
+class ExtractRenderSetup(plugin.MayaExtractorPlugin):
"""
Produce renderSetup template file
@@ -13,7 +13,6 @@ class ExtractRenderSetup(publish.Extractor):
"""
label = "Extract RenderSetup"
- hosts = ["maya"]
families = ["rendersetup"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_rig.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_rig.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_rig.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_rig.py
index 305f4698c6..3f96d7123d 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_rig.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_rig.py
@@ -2,17 +2,15 @@
"""Extract rig as Maya Scene."""
import os
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
-
-class ExtractRig(publish.Extractor):
+class ExtractRig(plugin.MayaExtractorPlugin):
"""Extract rig as Maya Scene."""
label = "Extract Rig (Maya Scene)"
- hosts = ["maya"]
families = ["rig"]
scene_type = "ma"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_skeleton_mesh.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_skeleton_mesh.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_skeleton_mesh.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_skeleton_mesh.py
index a6811d6a6f..e496d53d42 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_skeleton_mesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_skeleton_mesh.py
@@ -4,12 +4,12 @@ import os
from maya import cmds # noqa
import pyblish.api
-from ayon_core.pipeline import publish
from ayon_core.pipeline.publish import OptionalPyblishPluginMixin
-from ayon_core.hosts.maya.api import fbx
+from ayon_maya.api import fbx
+from ayon_maya.api import plugin
-class ExtractSkeletonMesh(publish.Extractor,
+class ExtractSkeletonMesh(plugin.MayaExtractorPlugin,
OptionalPyblishPluginMixin):
"""Extract Rig in FBX format from Maya.
@@ -21,7 +21,6 @@ class ExtractSkeletonMesh(publish.Extractor,
"""
order = pyblish.api.ExtractorOrder
label = "Extract Skeleton Mesh"
- hosts = ["maya"]
families = ["rig.fbx"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_thumbnail.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_thumbnail.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_thumbnail.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_thumbnail.py
index d3140487a6..c2ffedd67c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_thumbnail.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_thumbnail.py
@@ -1,12 +1,12 @@
-import os
import glob
+import os
import tempfile
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class ExtractThumbnail(publish.Extractor):
+class ExtractThumbnail(plugin.MayaExtractorPlugin):
"""Extract viewport thumbnail.
Takes review camera and creates a thumbnail based on viewport
@@ -15,7 +15,6 @@ class ExtractThumbnail(publish.Extractor):
"""
label = "Thumbnail"
- hosts = ["maya"]
families = ["review"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_skeletalmesh_abc.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_skeletalmesh_abc.py
index b5cc7745a1..a5d9303052 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_skeletalmesh_abc.py
@@ -2,21 +2,16 @@
"""Create Unreal Skeletal Mesh data to be extracted as FBX."""
import os
+from ayon_maya.api.alembic import extract_alembic
+from ayon_maya.api.lib import maintained_selection, suspended_refresh
+from ayon_maya.api import plugin
from maya import cmds # noqa
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.alembic import extract_alembic
-from ayon_core.hosts.maya.api.lib import (
- suspended_refresh,
- maintained_selection
-)
-
-class ExtractUnrealSkeletalMeshAbc(publish.Extractor):
+class ExtractUnrealSkeletalMeshAbc(plugin.MayaExtractorPlugin):
"""Extract Unreal Skeletal Mesh as FBX from Maya. """
label = "Extract Unreal Skeletal Mesh - Alembic"
- hosts = ["maya"]
families = ["skeletalMesh"]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py
index 6292afcf41..36324d3511 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_skeletalmesh_fbx.py
@@ -3,12 +3,10 @@
import os
from contextlib import contextmanager
-from maya import cmds # noqa
-
import pyblish.api
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import fbx
+from ayon_maya.api import fbx
+from ayon_maya.api import plugin
+from maya import cmds # noqa
@contextmanager
@@ -21,7 +19,7 @@ def renamed(original_name, renamed_name):
cmds.rename(renamed_name, original_name)
-class ExtractUnrealSkeletalMeshFbx(publish.Extractor):
+class ExtractUnrealSkeletalMeshFbx(plugin.MayaExtractorPlugin):
"""Extract Unreal Skeletal Mesh as FBX from Maya. """
order = pyblish.api.ExtractorOrder - 0.1
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_staticmesh.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_staticmesh.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_unreal_staticmesh.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_staticmesh.py
index 9cf8a543f4..215f82b338 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_staticmesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_staticmesh.py
@@ -2,19 +2,14 @@
"""Create Unreal Static Mesh data to be extracted as FBX."""
import os
+import pyblish.api
+from ayon_maya.api import fbx
+from ayon_maya.api.lib import maintained_selection, parent_nodes
+from ayon_maya.api import plugin
from maya import cmds # noqa
-import pyblish.api
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import (
- parent_nodes,
- maintained_selection
-)
-from ayon_core.hosts.maya.api import fbx
-
-
-class ExtractUnrealStaticMesh(publish.Extractor):
+class ExtractUnrealStaticMesh(plugin.MayaExtractorPlugin):
"""Extract Unreal Static Mesh as FBX from Maya. """
order = pyblish.api.ExtractorOrder - 0.1
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_yeticache.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_yeticache.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_unreal_yeticache.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_yeticache.py
index 9a6b4ebaed..79f47fbe9b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_unreal_yeticache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_unreal_yeticache.py
@@ -1,18 +1,16 @@
import os
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-
-class ExtractUnrealYetiCache(publish.Extractor):
+class ExtractUnrealYetiCache(plugin.MayaExtractorPlugin):
"""Producing Yeti cache files using scene time range.
This will extract Yeti cache file sequence and fur settings.
"""
label = "Extract Yeti Cache (Unreal)"
- hosts = ["maya"]
families = ["yeticacheUE"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_vrayproxy.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_vrayproxy.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_vrayproxy.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_vrayproxy.py
index d16f9e8701..d6f1fd6698 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_vrayproxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_vrayproxy.py
@@ -1,12 +1,11 @@
import os
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import maintained_selection
-
-class ExtractVRayProxy(publish.Extractor):
+class ExtractVRayProxy(plugin.MayaExtractorPlugin):
"""Extract the content of the instance to a vrmesh file
Things to pay attention to:
@@ -15,7 +14,6 @@ class ExtractVRayProxy(publish.Extractor):
"""
label = "VRay Proxy (.vrmesh)"
- hosts = ["maya"]
families = ["vrayproxy.vrmesh"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_vrayscene.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_vrayscene.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_vrayscene.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_vrayscene.py
index 023a15e67a..785cb4c37c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_vrayscene.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_vrayscene.py
@@ -3,18 +3,16 @@
import os
import re
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.render_setup_tools import export_in_rs_layer
-from ayon_core.hosts.maya.api.lib import maintained_selection
-
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
+from ayon_maya.api.render_setup_tools import export_in_rs_layer
from maya import cmds
-class ExtractVrayscene(publish.Extractor):
+class ExtractVrayscene(plugin.MayaExtractorPlugin):
"""Extractor for vrscene."""
label = "VRay Scene (.vrscene)"
- hosts = ["maya"]
families = ["vrayscene_layer"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_workfile_xgen.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_workfile_xgen.py
index 54d295b479..e6df19c7f1 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_workfile_xgen.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_workfile_xgen.py
@@ -1,15 +1,14 @@
+import copy
import os
import shutil
-import copy
-
-from maya import cmds
import pyblish.api
-from ayon_core.hosts.maya.api.alembic import extract_alembic
-from ayon_core.pipeline import publish
+from ayon_maya.api.alembic import extract_alembic
+from ayon_maya.api import plugin
+from maya import cmds
-class ExtractWorkfileXgen(publish.Extractor):
+class ExtractWorkfileXgen(plugin.MayaExtractorPlugin):
"""Extract Workfile Xgen.
When submitting a render, we need to prep Xgen side car files.
@@ -19,7 +18,6 @@ class ExtractWorkfileXgen(publish.Extractor):
order = pyblish.api.ExtractorOrder - 0.499
label = "Extract Workfile Xgen"
families = ["workfile"]
- hosts = ["maya"]
def get_render_max_frame_range(self, context):
"""Return start to end frame range including all renderlayers in
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_xgen.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_xgen.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_xgen.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_xgen.py
index b672089a63..bb700bbdec 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_xgen.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_xgen.py
@@ -1,17 +1,19 @@
-import os
import copy
+import os
import tempfile
-from maya import cmds
import xgenm
-
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection, attribute_values, write_xgen_file, delete_after
+from ayon_maya.api.lib import (
+ attribute_values,
+ delete_after,
+ maintained_selection,
+ write_xgen_file,
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ExtractXgen(publish.Extractor):
+class ExtractXgen(plugin.MayaExtractorPlugin):
"""Extract Xgen
Workflow:
@@ -23,7 +25,6 @@ class ExtractXgen(publish.Extractor):
"""
label = "Extract Xgen"
- hosts = ["maya"]
families = ["xgen"]
scene_type = "ma"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_yeti_cache.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_yeti_cache.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_yeti_cache.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_yeti_cache.py
index b9cd7a1be5..b84867316c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_yeti_cache.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_yeti_cache.py
@@ -1,19 +1,17 @@
-import os
import json
+import os
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-
-class ExtractYetiCache(publish.Extractor):
+class ExtractYetiCache(plugin.MayaExtractorPlugin):
"""Producing Yeti cache files using scene time range.
This will extract Yeti cache file sequence and fur settings.
"""
label = "Extract Yeti Cache"
- hosts = ["maya"]
families = ["yetiRig", "yeticache"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_yeti_rig.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_yeti_rig.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/extract_yeti_rig.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/extract_yeti_rig.py
index 0b67117ebc..640b37b667 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/extract_yeti_rig.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_yeti_rig.py
@@ -1,15 +1,14 @@
# -*- coding: utf-8 -*-
"""Extract Yeti rig."""
-import os
-import json
import contextlib
+import json
+import os
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline import publish
-from ayon_core.hosts.maya.api import lib
-
@contextlib.contextmanager
def disconnect_plugs(settings, members):
@@ -90,11 +89,10 @@ def yetigraph_attribute_values(assumed_destination, resources):
pass
-class ExtractYetiRig(publish.Extractor):
+class ExtractYetiRig(plugin.MayaExtractorPlugin):
"""Extract the Yeti rig to a Maya Scene and write the Yeti rig data."""
label = "Extract Yeti Rig"
- hosts = ["maya"]
families = ["yetiRig"]
scene_type = "ma"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/submit_maya_remote_publish_deadline.xml b/server_addon/maya/client/ayon_maya/plugins/publish/help/submit_maya_remote_publish_deadline.xml
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/help/submit_maya_remote_publish_deadline.xml
rename to server_addon/maya/client/ayon_maya/plugins/publish/help/submit_maya_remote_publish_deadline.xml
index e92320ccdc..fa908fe425 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/help/submit_maya_remote_publish_deadline.xml
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/help/submit_maya_remote_publish_deadline.xml
@@ -13,4 +13,4 @@ Check all failing plugins (should be highlighted in red) and fix issues if possi
-
\ No newline at end of file
+
diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/validate_animation_out_set_related_node_ids.xml b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_animation_out_set_related_node_ids.xml
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/help/validate_animation_out_set_related_node_ids.xml
rename to server_addon/maya/client/ayon_maya/plugins/publish/help/validate_animation_out_set_related_node_ids.xml
index a855dd90a5..cdaf97b8f4 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/help/validate_animation_out_set_related_node_ids.xml
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_animation_out_set_related_node_ids.xml
@@ -6,13 +6,13 @@
Meshes are detected where the (deformed) mesh has a different `cbId` than
the same mesh in its deformation history.
-Theses should normally be the same.
+These should normally be the same.
### How to repair?
By using the repair action the IDs from the shape in history will be
copied to the deformed shape. For **animation** instances using the
-repair action usually is usually the correct fix.
+repair action is usually the correct fix.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/validate_maya_units.xml b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_maya_units.xml
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/publish/help/validate_maya_units.xml
rename to server_addon/maya/client/ayon_maya/plugins/publish/help/validate_maya_units.xml
diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/validate_mesh_non_manifold.xml b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_mesh_non_manifold.xml
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/publish/help/validate_mesh_non_manifold.xml
rename to server_addon/maya/client/ayon_maya/plugins/publish/help/validate_mesh_non_manifold.xml
diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/validate_node_ids.xml b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_node_ids.xml
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/publish/help/validate_node_ids.xml
rename to server_addon/maya/client/ayon_maya/plugins/publish/help/validate_node_ids.xml
diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/validate_rig_out_set_node_ids.xml b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_rig_out_set_node_ids.xml
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/help/validate_rig_out_set_node_ids.xml
rename to server_addon/maya/client/ayon_maya/plugins/publish/help/validate_rig_out_set_node_ids.xml
index 374b8e59ae..956a7adb3b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/help/validate_rig_out_set_node_ids.xml
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_rig_out_set_node_ids.xml
@@ -6,7 +6,7 @@
Meshes are detected in the **rig** where the (deformed) mesh has a different
`cbId` than the same mesh in its deformation history.
-Theses should normally be the same.
+These should normally be the same.
### How to repair?
diff --git a/client/ayon_core/hosts/maya/plugins/publish/help/validate_skeletalmesh_hierarchy.xml b/server_addon/maya/client/ayon_maya/plugins/publish/help/validate_skeletalmesh_hierarchy.xml
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/publish/help/validate_skeletalmesh_hierarchy.xml
rename to server_addon/maya/client/ayon_maya/plugins/publish/help/validate_skeletalmesh_hierarchy.xml
diff --git a/client/ayon_core/hosts/maya/plugins/publish/increment_current_file_deadline.py b/server_addon/maya/client/ayon_maya/plugins/publish/increment_current_file_deadline.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/increment_current_file_deadline.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/increment_current_file_deadline.py
index a9378df8e1..66019c4837 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/increment_current_file_deadline.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/increment_current_file_deadline.py
@@ -1,7 +1,8 @@
import pyblish.api
+from ayon_maya.api import plugin
-class IncrementCurrentFileDeadline(pyblish.api.ContextPlugin):
+class IncrementCurrentFileDeadline(plugin.MayaContextPlugin):
"""Increment the current file.
Saves the current maya scene with an increased version number.
@@ -10,15 +11,14 @@ class IncrementCurrentFileDeadline(pyblish.api.ContextPlugin):
label = "Increment current file"
order = pyblish.api.IntegratorOrder + 9.0
- hosts = ["maya"]
families = ["workfile"]
optional = True
def process(self, context):
- from maya import cmds
from ayon_core.lib import version_up
from ayon_core.pipeline.publish import get_errored_plugins_from_context
+ from maya import cmds
errored_plugins = get_errored_plugins_from_context(context)
if any(plugin.__name__ == "MayaSubmitDeadline"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/reset_xgen_attributes.py b/server_addon/maya/client/ayon_maya/plugins/publish/reset_xgen_attributes.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/reset_xgen_attributes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/reset_xgen_attributes.py
index 759aa23258..ac9e1beeec 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/reset_xgen_attributes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/reset_xgen_attributes.py
@@ -1,9 +1,9 @@
+import pyblish.api
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-
-class ResetXgenAttributes(pyblish.api.InstancePlugin):
+class ResetXgenAttributes(plugin.MayaInstancePlugin):
"""Reset Xgen attributes.
When the incremental save of the workfile triggers, the Xgen attributes
diff --git a/client/ayon_core/hosts/maya/plugins/publish/save_scene.py b/server_addon/maya/client/ayon_maya/plugins/publish/save_scene.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/save_scene.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/save_scene.py
index eb7c06a113..9c23fcff85 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/save_scene.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/save_scene.py
@@ -1,18 +1,16 @@
import pyblish.api
from ayon_core.pipeline.workfile.lock_workfile import (
is_workfile_lock_enabled,
- remove_workfile_lock
+ remove_workfile_lock,
)
+from ayon_maya.api import plugin
-class SaveCurrentScene(pyblish.api.ContextPlugin):
- """Save current scene
-
- """
+class SaveCurrentScene(plugin.MayaContextPlugin):
+ """Save current scene."""
label = "Save current file"
order = pyblish.api.ExtractorOrder - 0.49
- hosts = ["maya"]
families = ["renderlayer", "workfile"]
def process(self, context):
diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/validate_alembic_options_defaults.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_alembic_options_defaults.py
new file mode 100644
index 0000000000..fd4e2254a7
--- /dev/null
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_alembic_options_defaults.py
@@ -0,0 +1,130 @@
+import inspect
+
+import pyblish.api
+from ayon_core.pipeline import OptionalPyblishPluginMixin
+from ayon_core.pipeline.publish import PublishValidationError, RepairAction
+from ayon_maya.api import plugin
+
+
+class ValidateAlembicDefaultsPointcache(
+ plugin.MayaInstancePlugin, OptionalPyblishPluginMixin
+):
+ """Validate the attributes on the instance are defaults.
+
+ The defaults are defined in the project settings.
+ """
+
+ order = pyblish.api.ValidatorOrder
+ families = ["pointcache"]
+ label = "Validate Alembic Options Defaults"
+ actions = [RepairAction]
+ optional = True
+
+ plugin_name = "ExtractAlembic"
+
+ @classmethod
+ def _get_settings(cls, context):
+ maya_settings = context.data["project_settings"]["maya"]
+ settings = maya_settings["publish"]["ExtractAlembic"]
+ return settings
+
+ @classmethod
+ def _get_publish_attributes(cls, instance):
+ return instance.data["publish_attributes"][cls.plugin_name]
+
+ def process(self, instance):
+ if not self.is_active(instance.data):
+ return
+
+ settings = self._get_settings(instance.context)
+ attributes = self._get_publish_attributes(instance)
+
+ invalid = {}
+ for key, value in attributes.items():
+ if key not in settings:
+ # This may occur if attributes have changed over time and an
+ # existing instance has older legacy attributes that do not
+ # match the current settings definition.
+ self.log.warning(
+ "Publish attribute %s not found in Alembic Export "
+ "default settings. Ignoring validation for attribute.",
+ key
+ )
+ continue
+
+ default_value = settings[key]
+
+ # Lists are best to compared sorted since we can't rely on
+ # the order of the items.
+ if isinstance(value, list):
+ value = sorted(value)
+ default_value = sorted(default_value)
+
+ if value != default_value:
+ invalid[key] = value, default_value
+
+ if invalid:
+ non_defaults = "\n".join(
+ f"- {key}: {value} \t(default: {default_value})"
+ for key, (value, default_value) in invalid.items()
+ )
+
+ raise PublishValidationError(
+ "Alembic extract options differ from default values:\n"
+ f"{non_defaults}",
+ description=self.get_description()
+ )
+
+ @staticmethod
+ def get_description():
+ return inspect.cleandoc(
+ """### Alembic Extract settings differ from defaults
+
+ The alembic export options differ from the project default values.
+
+ If this is intentional you can disable this validation by
+ disabling **Validate Alembic Options Default**.
+
+ If not you may use the "Repair" action to revert all the options to
+ their default values.
+
+ """
+ )
+
+ @classmethod
+ def repair(cls, instance):
+ # Find create instance twin.
+ create_context = instance.context.data["create_context"]
+ create_instance = create_context.get_instance_by_id(
+ instance.data["instance_id"]
+ )
+
+ # Set the settings values on the create context then save to workfile.
+ settings = cls._get_settings(instance.context)
+ attributes = cls._get_publish_attributes(create_instance)
+ for key in attributes:
+ if key not in settings:
+ # This may occur if attributes have changed over time and an
+ # existing instance has older legacy attributes that do not
+ # match the current settings definition.
+ cls.log.warning(
+ "Publish attribute %s not found in Alembic Export "
+ "default settings. Ignoring repair for attribute.",
+ key
+ )
+ continue
+ attributes[key] = settings[key]
+
+ create_context.save_changes()
+
+
+class ValidateAlembicDefaultsAnimation(
+ ValidateAlembicDefaultsPointcache
+):
+ """Validate the attributes on the instance are defaults.
+
+ The defaults are defined in the project settings.
+ """
+ label = "Validate Alembic Options Defaults"
+ families = ["animation"]
+ plugin_name = "ExtractAnimation"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animation_content.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_animation_content.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_animation_content.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_animation_content.py
index ea989bbcf3..b10a1a2bb7 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_animation_content.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_animation_content.py
@@ -1,13 +1,13 @@
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
ValidateContentsOrder,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateAnimationContent(pyblish.api.InstancePlugin,
+class ValidateAnimationContent(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Adheres to the content of 'animation' product type
@@ -17,10 +17,9 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["animation"]
label = "Animation Content"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
@classmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_animation_out_set_related_node_ids.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_animation_out_set_related_node_ids.py
index 7ecd602662..40f03b2690 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_animation_out_set_related_node_ids.py
@@ -1,19 +1,18 @@
+import ayon_maya.api.action
import maya.cmds as cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishXmlValidationError,
RepairAction,
ValidateContentsOrder,
- PublishXmlValidationError,
- OptionalPyblishPluginMixin,
+ apply_plugin_settings_automatically,
get_plugin_settings,
- apply_plugin_settings_automatically
)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin,
+class ValidateOutRelatedNodeIds(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate if deformed shapes have related IDs to the original shapes
@@ -29,7 +28,7 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin,
hosts = ['maya']
label = 'Animation Out Set Related Node Ids'
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.SelectInvalidAction,
RepairAction
]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_arnold_scene_source.py
similarity index 64%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_arnold_scene_source.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_arnold_scene_source.py
index 92b4922492..edc4161dff 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_arnold_scene_source.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_arnold_scene_source.py
@@ -1,30 +1,55 @@
import pyblish.api
from ayon_core.pipeline.publish import (
- ValidateContentsOrder, PublishValidationError
+ PublishValidationError,
+ ValidateContentsOrder,
)
+from ayon_maya.api.lib import is_visible
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateArnoldSceneSource(pyblish.api.InstancePlugin):
+class ValidateArnoldSceneSource(plugin.MayaInstancePlugin):
"""Validate Arnold Scene Source.
- We require at least 1 root node/parent for the meshes. This is to ensure we
- can duplicate the nodes and preserve the names.
+ Ensure no nodes are hidden.
+ """
- If using proxies we need the nodes to share the same names and not be
+ order = ValidateContentsOrder
+ families = ["ass", "assProxy"]
+ label = "Validate Arnold Scene Source"
+
+ def process(self, instance):
+ # Validate against having nodes hidden, which will result in the
+ # extraction to ignore the node.
+ nodes = instance.data["members"] + instance.data.get("proxy", [])
+ nodes = [x for x in nodes if cmds.objectType(x, isAType='dagNode')]
+ hidden_nodes = [
+ x for x in nodes if not is_visible(x, intermediateObject=False)
+ ]
+ if hidden_nodes:
+ raise PublishValidationError(
+ "Found hidden nodes:\n\n{}\n\nPlease unhide for"
+ " publishing.".format("\n".join(hidden_nodes))
+ )
+
+
+class ValidateArnoldSceneSourceProxy(pyblish.api.InstancePlugin):
+ """Validate Arnold Scene Source Proxy.
+
+ When using proxies we need the nodes to share the same names and not be
parent to the world. This ends up needing at least two groups with content
nodes and proxy nodes in another.
"""
order = ValidateContentsOrder
hosts = ["maya"]
- families = ["ass"]
- label = "Validate Arnold Scene Source"
+ families = ["assProxy"]
+ label = "Validate Arnold Scene Source Proxy"
def _get_nodes_by_name(self, nodes):
ungrouped_nodes = []
nodes_by_name = {}
parents = []
- same_named_nodes = {}
for node in nodes:
node_split = node.split("|")
if len(node_split) == 2:
@@ -35,33 +60,16 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin):
parents.append(parent)
node_name = node.rsplit("|", 1)[-1].rsplit(":", 1)[-1]
-
- # Check for same same nodes, which can happen in different
- # hierarchies.
- if node_name in nodes_by_name:
- try:
- same_named_nodes[node_name].append(node)
- except KeyError:
- same_named_nodes[node_name] = [
- nodes_by_name[node_name], node
- ]
-
nodes_by_name[node_name] = node
- if same_named_nodes:
- message = "Found nodes with the same name:"
- for name, nodes in same_named_nodes.items():
- message += "\n\n\"{}\":\n{}".format(name, "\n".join(nodes))
-
- raise PublishValidationError(message)
-
return ungrouped_nodes, nodes_by_name, parents
def process(self, instance):
+ # Validate against nodes directly parented to world.
ungrouped_nodes = []
nodes, content_nodes_by_name, content_parents = (
- self._get_nodes_by_name(instance.data["contentMembers"])
+ self._get_nodes_by_name(instance.data["members"])
)
ungrouped_nodes.extend(nodes)
@@ -70,24 +78,21 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin):
)
ungrouped_nodes.extend(nodes)
- # Validate against nodes directly parented to world.
if ungrouped_nodes:
raise PublishValidationError(
"Found nodes parented to the world: {}\n"
"All nodes need to be grouped.".format(ungrouped_nodes)
)
- # Proxy validation.
- if not instance.data.get("proxy", []):
- return
-
# Validate for content and proxy nodes amount being the same.
- if len(instance.data["contentMembers"]) != len(instance.data["proxy"]):
+ if len(instance.data["members"]) != len(instance.data["proxy"]):
raise PublishValidationError(
"Amount of content nodes ({}) and proxy nodes ({}) needs to "
- "be the same.".format(
- len(instance.data["contentMembers"]),
- len(instance.data["proxy"])
+ "be the same.\nContent nodes: {}\nProxy nodes:{}".format(
+ len(instance.data["members"]),
+ len(instance.data["proxy"]),
+ instance.data["members"],
+ instance.data["proxy"]
)
)
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_arnold_scene_source_cbid.py
similarity index 80%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_arnold_scene_source_cbid.py
index a9d896952d..8da8813b0d 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_arnold_scene_source_cbid.py
@@ -1,14 +1,14 @@
-import pyblish.api
-from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
+ OptionalPyblishPluginMixin,
PublishValidationError,
RepairAction,
- OptionalPyblishPluginMixin
+ ValidateContentsOrder,
)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin,
+class ValidateArnoldSceneSourceCbid(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate Arnold Scene Source Cbid.
@@ -16,8 +16,7 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
- families = ["ass"]
+ families = ["assProxy"]
label = "Validate Arnold Scene Source CBID"
actions = [RepairAction]
optional = False
@@ -40,15 +39,11 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin,
@classmethod
def get_invalid_couples(cls, instance):
- content_nodes_by_name = cls._get_nodes_by_name(
- instance.data["contentMembers"]
- )
- proxy_nodes_by_name = cls._get_nodes_by_name(
- instance.data.get("proxy", [])
- )
+ nodes_by_name = cls._get_nodes_by_name(instance.data["members"])
+ proxy_nodes_by_name = cls._get_nodes_by_name(instance.data["proxy"])
invalid_couples = []
- for content_name, content_node in content_nodes_by_name.items():
+ for content_name, content_node in nodes_by_name.items():
proxy_node = proxy_nodes_by_name.get(content_name, None)
if not proxy_node:
@@ -70,7 +65,7 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin,
if not self.is_active(instance.data):
return
# Proxy validation.
- if not instance.data.get("proxy", []):
+ if not instance.data["proxy"]:
return
# Validate for proxy nodes sharing the same cbId as content nodes.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_ass_relative_paths.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_ass_relative_paths.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_ass_relative_paths.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_ass_relative_paths.py
index 6e65eee592..36c220f862 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_ass_relative_paths.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_ass_relative_paths.py
@@ -4,21 +4,20 @@ import types
import maya.cmds as cmds
from mtoa.core import createOptions
-import pyblish.api
from ayon_core.pipeline.publish import (
RepairAction,
ValidateContentsOrder,
PublishValidationError,
OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateAssRelativePaths(pyblish.api.InstancePlugin,
+class ValidateAssRelativePaths(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure exporting ass file has set relative texture paths"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['ass']
label = "ASS has relative texture paths"
actions = [RepairAction]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_assembly_name.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_name.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_assembly_name.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_name.py
index c829f4bf74..4dfe7214bf 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_assembly_name.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_name.py
@@ -1,13 +1,14 @@
-import pyblish.api
+import ayon_maya.api.action
import maya.cmds as cmds
-import ayon_core.hosts.maya.api.action
+import pyblish.api
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateAssemblyName(pyblish.api.InstancePlugin,
+class ValidateAssemblyName(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
""" Ensure Assembly name ends with `GRP`
@@ -17,7 +18,7 @@ class ValidateAssemblyName(pyblish.api.InstancePlugin,
label = "Validate Assembly Name"
order = pyblish.api.ValidatorOrder
families = ["assembly"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
active = False
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_assembly_namespaces.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_namespaces.py
similarity index 81%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_assembly_namespaces.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_namespaces.py
index 814a8295c4..324b12a207 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_assembly_namespaces.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_namespaces.py
@@ -1,13 +1,15 @@
+import ayon_maya.api.action
import pyblish.api
-import ayon_core.hosts.maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateAssemblyNamespaces(pyblish.api.InstancePlugin,
+
+class ValidateAssemblyNamespaces(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
- """Ensure namespaces are not nested
+ """Ensure namespaces are not nested.
In the outliner an item in a normal namespace looks as following:
props_desk_01_:modelDefault
@@ -21,7 +23,7 @@ class ValidateAssemblyNamespaces(pyblish.api.InstancePlugin,
label = "Validate Assembly Namespaces"
order = pyblish.api.ValidatorOrder
families = ["assembly"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_assembly_transforms.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_transforms.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_assembly_transforms.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_transforms.py
index 3bcae5de49..7fc14560f7 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_assembly_transforms.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_assembly_transforms.py
@@ -1,15 +1,15 @@
+import ayon_maya.api.action
import pyblish.api
-from maya import cmds
-
-import ayon_core.hosts.maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
RepairAction,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin,
+class ValidateAssemblyModelTransforms(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Verify only root nodes of the loaded asset have transformations.
@@ -32,7 +32,7 @@ class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin,
order = pyblish.api.ValidatorOrder + 0.49
label = "Assembly Model Transforms"
families = ["assembly"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
prompt_message = ("You are about to reset the matrix to the default values."
@@ -53,7 +53,7 @@ class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin,
@classmethod
def get_invalid(cls, instance):
- from ayon_core.hosts.maya.api import lib
+ from ayon_maya.api import lib
# Get all transforms in the loaded containers
container_roots = cmds.listRelatives(instance.data["nodesHierarchy"],
@@ -98,10 +98,9 @@ class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin,
"""
+ from ayon_maya.api import lib
from qtpy import QtWidgets
- from ayon_core.hosts.maya.api import lib
-
# Store namespace in variable, cosmetics thingy
choice = QtWidgets.QMessageBox.warning(
None,
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_attributes.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_attributes.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_attributes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_attributes.py
index 1514972159..8d4d8323ce 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_attributes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_attributes.py
@@ -1,16 +1,18 @@
import json
from collections import defaultdict
-import pyblish.api
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ RepairAction,
+ ValidateContentsOrder,
+)
+from ayon_maya.api.lib import set_attribute
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.hosts.maya.api.lib import set_attribute
-from ayon_core.pipeline.publish import (
- OptionalPyblishPluginMixin, PublishValidationError, RepairAction,
- ValidateContentsOrder)
-
-class ValidateAttributes(pyblish.api.InstancePlugin,
+class ValidateAttributes(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure attributes are consistent.
@@ -25,7 +27,6 @@ class ValidateAttributes(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
label = "Validate Attributes"
- hosts = ["maya"]
actions = [RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_camera_attributes.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_camera_attributes.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_camera_attributes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_camera_attributes.py
index 5fd8772a96..8c3f3800cc 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_camera_attributes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_camera_attributes.py
@@ -1,15 +1,14 @@
-import pyblish.api
-from maya import cmds
-
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
ValidateContentsOrder,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateCameraAttributes(pyblish.api.InstancePlugin,
+class ValidateCameraAttributes(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates Camera has no invalid attribute keys or values.
@@ -23,7 +22,7 @@ class ValidateCameraAttributes(pyblish.api.InstancePlugin,
families = ['camera']
hosts = ['maya']
label = 'Camera Attributes'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
DEFAULTS = [
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_camera_contents.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_camera_contents.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_camera_contents.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_camera_contents.py
index 0f14a057f9..42a5ef1769 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_camera_contents.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_camera_contents.py
@@ -1,14 +1,14 @@
-import pyblish.api
from maya import cmds
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
PublishValidationError,
ValidateContentsOrder,
OptionalPyblishPluginMixin)
+from ayon_maya.api import plugin
-class ValidateCameraContents(pyblish.api.InstancePlugin,
+class ValidateCameraContents(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates Camera instance contents.
@@ -21,9 +21,8 @@ class ValidateCameraContents(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
families = ['camera']
- hosts = ['maya']
label = 'Camera Contents'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
validate_shapes = True
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_color_sets.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_color_sets.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_color_sets.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_color_sets.py
index f70b46f89e..f95e27def1 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_color_sets.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_color_sets.py
@@ -1,16 +1,15 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
OptionalPyblishPluginMixin,
PublishValidationError,
- RepairAction
+ RepairAction,
+ ValidateMeshOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateColorSets(pyblish.api.InstancePlugin,
+class ValidateColorSets(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate all meshes in the instance have unlocked normals
@@ -20,11 +19,10 @@ class ValidateColorSets(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh ColorSets'
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction, RepairAction
+ ayon_maya.api.action.SelectInvalidAction, RepairAction
]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_current_renderlayer_renderable.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_current_renderlayer_renderable.py
index 045e22545c..6c599d398d 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_current_renderlayer_renderable.py
@@ -1,16 +1,16 @@
import inspect
import pyblish.api
-
-from maya import cmds
from ayon_core.pipeline.publish import (
- context_plugin_should_run,
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
+ context_plugin_should_run,
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin,
+class ValidateCurrentRenderLayerIsRenderable(plugin.MayaContextPlugin,
OptionalPyblishPluginMixin):
"""Validate if current render layer has a renderable camera.
@@ -25,7 +25,6 @@ class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin,
label = "Current Render Layer Has Renderable Camera"
order = pyblish.api.ValidatorOrder
- hosts = ["maya"]
families = ["renderlayer"]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_cycle_error.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_cycle_error.py
similarity index 74%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_cycle_error.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_cycle_error.py
index f969ff533b..0b870993e9 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_cycle_error.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_cycle_error.py
@@ -1,13 +1,15 @@
-import pyblish.api
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api.lib import maintained_selection
+from ayon_maya.api import plugin
from maya import cmds
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api.lib import maintained_selection
-from ayon_core.pipeline.publish import (
- OptionalPyblishPluginMixin, PublishValidationError, ValidateContentsOrder)
-
-class ValidateCycleError(pyblish.api.InstancePlugin,
+class ValidateCycleError(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate nodes produce no cycle errors."""
@@ -15,7 +17,7 @@ class ValidateCycleError(pyblish.api.InstancePlugin,
label = "Cycle Errors"
hosts = ["maya"]
families = ["rig"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_frame_range.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_frame_range.py
index 5736e726e9..90bdef4107 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_frame_range.py
@@ -1,20 +1,16 @@
-import pyblish.api
-
-from maya import cmds
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-from ayon_core.hosts.maya.api.lib_rendersetup import (
- get_attr_overrides,
- get_attr_in_layer,
)
+from ayon_maya.api.lib_rendersetup import get_attr_in_layer, get_attr_overrides
+from ayon_maya.api import plugin
+from maya import cmds
from maya.app.renderSetup.model.override import AbsOverride
-class ValidateFrameRange(pyblish.api.InstancePlugin,
+class ValidateFrameRange(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates the frame ranges.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_glsl_material.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_glsl_material.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_glsl_material.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_glsl_material.py
index 3735dbb74c..e94cb3e663 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_glsl_material.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_glsl_material.py
@@ -1,15 +1,15 @@
import os
+
+from ayon_core.pipeline import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+)
+from ayon_core.pipeline.publish import RepairAction, ValidateContentsOrder
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.pipeline.publish import (
- RepairAction,
- ValidateContentsOrder
-)
-from ayon_core.pipeline import PublishValidationError, OptionalPyblishPluginMixin
-
-class ValidateGLSLMaterial(pyblish.api.InstancePlugin,
+class ValidateGLSLMaterial(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""
Validate if the asset uses GLSL Shader
@@ -17,7 +17,6 @@ class ValidateGLSLMaterial(pyblish.api.InstancePlugin,
order = ValidateContentsOrder + 0.1
families = ['gltf']
- hosts = ['maya']
label = 'GLSL Shader for GLTF'
actions = [RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_glsl_plugin.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_glsl_plugin.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_glsl_plugin.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_glsl_plugin.py
index d783da8b5c..aaea616631 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_glsl_plugin.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_glsl_plugin.py
@@ -1,16 +1,15 @@
-
from maya import cmds
-import pyblish.api
from ayon_core.pipeline.publish import (
RepairAction,
ValidateContentsOrder,
PublishValidationError,
OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateGLSLPlugin(pyblish.api.InstancePlugin,
+class ValidateGLSLPlugin(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""
Validate if the asset uses GLSL Shader
@@ -18,7 +17,6 @@ class ValidateGLSLPlugin(pyblish.api.InstancePlugin,
order = ValidateContentsOrder + 0.15
families = ['gltf']
- hosts = ['maya']
label = 'maya2glTF plugin'
actions = [RepairAction]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_has_members.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_has_members.py
similarity index 81%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_instance_has_members.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_has_members.py
index 16e04af446..baca2a9008 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_has_members.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_has_members.py
@@ -1,18 +1,17 @@
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ PublishValidationError,
ValidateContentsOrder,
- PublishValidationError
)
+from ayon_maya.api import plugin
-class ValidateInstanceHasMembers(pyblish.api.InstancePlugin):
+class ValidateInstanceHasMembers(plugin.MayaInstancePlugin):
"""Validates instance objectSet has *any* members."""
order = ValidateContentsOrder
- hosts = ["maya"]
label = 'Instance has members'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
@classmethod
def get_invalid(cls, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_in_context.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_in_context.py
index e6f4b908bb..5168c8496c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_in_context.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_in_context.py
@@ -2,17 +2,17 @@
"""Validate if instance asset is the same as context asset."""
from __future__ import absolute_import
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateInstanceInContext(pyblish.api.InstancePlugin,
+class ValidateInstanceInContext(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validator to check if instance asset match context asset.
@@ -26,9 +26,8 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
label = "Instance in same Context"
optional = True
- hosts = ["maya"]
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction, RepairAction
+ ayon_maya.api.action.SelectInvalidAction, RepairAction
]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_subset.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_subset.py
index df9ca0bf13..4c876079ff 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_instance_subset.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_instance_subset.py
@@ -1,11 +1,11 @@
-import pyblish.api
import string
import six
from ayon_core.pipeline.publish import (
+ PublishValidationError,
ValidateContentsOrder,
- PublishValidationError
)
+from ayon_maya.api import plugin
# Allow only characters, numbers and underscore
allowed = set(string.ascii_lowercase +
@@ -18,7 +18,7 @@ def validate_name(product_name):
return all(x in allowed for x in product_name)
-class ValidateSubsetName(pyblish.api.InstancePlugin):
+class ValidateSubsetName(plugin.MayaInstancePlugin):
"""Validates product name has only valid characters"""
order = ValidateContentsOrder
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_loaded_plugin.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_loaded_plugin.py
similarity index 67%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_loaded_plugin.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_loaded_plugin.py
index a05920a21e..60af00186e 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_loaded_plugin.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_loaded_plugin.py
@@ -7,15 +7,15 @@ from ayon_core.pipeline.publish import (
PublishValidationError,
OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateLoadedPlugin(pyblish.api.ContextPlugin,
+class ValidateLoadedPlugin(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure there are no unauthorized loaded plugins"""
label = "Loaded Plugin"
order = pyblish.api.ValidatorOrder
- host = ["maya"]
actions = [RepairContextAction]
optional = True
@@ -23,17 +23,17 @@ class ValidateLoadedPlugin(pyblish.api.ContextPlugin,
def get_invalid(cls, context):
invalid = []
- loaded_plugin = cmds.pluginInfo(query=True, listPlugins=True)
+ loaded_plugins = cmds.pluginInfo(query=True, listPlugins=True)
# get variable from AYON settings
whitelist_native_plugins = cls.whitelist_native_plugins
authorized_plugins = cls.authorized_plugins or []
- for plugin in loaded_plugin:
+ for maya_plugin in loaded_plugins:
if not whitelist_native_plugins and os.getenv('MAYA_LOCATION') \
- in cmds.pluginInfo(plugin, query=True, path=True):
+ in cmds.pluginInfo(maya_plugin, query=True, path=True):
continue
- if plugin not in authorized_plugins:
- invalid.append(plugin)
+ if maya_plugin not in authorized_plugins:
+ invalid.append(maya_plugin)
return invalid
@@ -50,6 +50,6 @@ class ValidateLoadedPlugin(pyblish.api.ContextPlugin,
def repair(cls, context):
"""Unload forbidden plugins"""
- for plugin in cls.get_invalid(context):
- cmds.pluginInfo(plugin, edit=True, autoload=False)
- cmds.unloadPlugin(plugin, force=True)
+ for maya_plugin in cls.get_invalid(context):
+ cmds.pluginInfo(maya_plugin, edit=True, autoload=False)
+ cmds.unloadPlugin(maya_plugin, force=True)
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_look_contents.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_contents.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_look_contents.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_look_contents.py
index a8d8ec373a..722f92b1b5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_look_contents.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_contents.py
@@ -1,15 +1,13 @@
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
PublishValidationError,
- ValidateContentsOrder
+ ValidateContentsOrder,
)
-
-
+from ayon_maya.api import plugin
from maya import cmds # noqa
-class ValidateLookContents(pyblish.api.InstancePlugin):
+class ValidateLookContents(plugin.MayaInstancePlugin):
"""Validate look instance contents
Rules:
@@ -25,9 +23,8 @@ class ValidateLookContents(pyblish.api.InstancePlugin):
order = ValidateContentsOrder
families = ['look']
- hosts = ['maya']
label = 'Look Data Contents'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
def process(self, instance):
"""Process all the nodes in the instance"""
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_look_default_shaders_connections.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_default_shaders_connections.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_look_default_shaders_connections.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_look_default_shaders_connections.py
index cfd4156124..ac936b36c7 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_look_default_shaders_connections.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_default_shaders_connections.py
@@ -1,13 +1,13 @@
-from maya import cmds
-
import pyblish.api
from ayon_core.pipeline.publish import (
+ PublishValidationError,
RepairContextAction,
- PublishValidationError
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateLookDefaultShadersConnections(pyblish.api.ContextPlugin):
+class ValidateLookDefaultShadersConnections(plugin.MayaContextPlugin):
"""Validate default shaders in the scene have their default connections.
For example the standardSurface1 or lambert1 (maya 2023 and before) could
@@ -22,7 +22,6 @@ class ValidateLookDefaultShadersConnections(pyblish.api.ContextPlugin):
order = pyblish.api.ValidatorOrder - 0.4999
families = ['look']
- hosts = ['maya']
label = 'Look Default Shader Connections'
actions = [RepairContextAction]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_look_id_reference_edits.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_id_reference_edits.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_look_id_reference_edits.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_look_id_reference_edits.py
index 7ae3b4b9b5..4763128f3f 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_look_id_reference_edits.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_id_reference_edits.py
@@ -1,16 +1,16 @@
from collections import defaultdict
-from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin):
+class ValidateLookIdReferenceEdits(plugin.MayaInstancePlugin):
"""Validate nodes in look have no reference edits to cbId.
Note:
@@ -22,9 +22,8 @@ class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin):
order = ValidateContentsOrder
families = ['look']
- hosts = ['maya']
label = 'Look Id Reference Edits'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
@classmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_look_no_default_shaders.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_no_default_shaders.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_look_no_default_shaders.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_look_no_default_shaders.py
index 3a67bbd72b..e4662dd498 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_look_no_default_shaders.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_no_default_shaders.py
@@ -1,14 +1,13 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- PublishValidationError
-)
-
-class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin):
+class ValidateLookNoDefaultShaders(plugin.MayaInstancePlugin):
"""Validate if any node has a connection to a default shader.
This checks whether the look has any members of:
@@ -28,9 +27,8 @@ class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin):
order = ValidateContentsOrder + 0.01
families = ['look']
- hosts = ['maya']
label = 'Look No Default Shaders'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
DEFAULT_SHADERS = {"lambert1", "initialShadingGroup",
"initialParticleSE", "particleCloud1"}
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_look_sets.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_sets.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_look_sets.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_look_sets.py
index dd7515c1fb..eae1664114 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_look_sets.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_sets.py
@@ -1,13 +1,13 @@
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
+import ayon_maya.api.action
+from ayon_maya.api import lib
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
PublishValidationError
)
+from ayon_maya.api import plugin
-class ValidateLookSets(pyblish.api.InstancePlugin):
+class ValidateLookSets(plugin.MayaInstancePlugin):
"""Validate if any sets relationships are not being collected.
A shader can be assigned to a node that is missing a Colorbleed ID.
@@ -42,9 +42,8 @@ class ValidateLookSets(pyblish.api.InstancePlugin):
order = ValidateContentsOrder
families = ['look']
- hosts = ['maya']
label = 'Look Sets'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
def process(self, instance):
"""Process all the nodes in the instance"""
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_look_shading_group.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_shading_group.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_look_shading_group.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_look_shading_group.py
index 070974aef5..2aecf64e01 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_look_shading_group.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_shading_group.py
@@ -1,16 +1,15 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateShadingEngine(pyblish.api.InstancePlugin,
+class ValidateShadingEngine(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate all shading engines are named after the surface material.
@@ -19,10 +18,9 @@ class ValidateShadingEngine(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
families = ["look"]
- hosts = ["maya"]
label = "Look Shading Engine Naming"
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction, RepairAction
+ ayon_maya.api.action.SelectInvalidAction, RepairAction
]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_look_single_shader.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_single_shader.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_look_single_shader.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_look_single_shader.py
index c0ffaaf9c0..d48c050d97 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_look_single_shader.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_look_single_shader.py
@@ -1,12 +1,13 @@
-import pyblish.api
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- PublishValidationError, ValidateContentsOrder)
-
-class ValidateSingleShader(pyblish.api.InstancePlugin):
+class ValidateSingleShader(plugin.MayaInstancePlugin):
"""Validate all nurbsSurfaces and meshes have exactly one shader assigned.
This will error if a shape has no shaders or more than one shader.
@@ -15,9 +16,8 @@ class ValidateSingleShader(pyblish.api.InstancePlugin):
order = ValidateContentsOrder
families = ['look']
- hosts = ['maya']
label = 'Look Single Shader Per Shape'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
# The default connections to check
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_maya_units.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_maya_units.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_maya_units.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_maya_units.py
index 47314b64ac..cb4df4b5d5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_maya_units.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_maya_units.py
@@ -1,23 +1,20 @@
+import ayon_maya.api.lib as mayalib
import maya.cmds as cmds
-
-import pyblish.api
-
-import ayon_core.hosts.maya.api.lib as mayalib
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishXmlValidationError,
RepairContextAction,
ValidateSceneOrder,
- PublishXmlValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateMayaUnits(pyblish.api.ContextPlugin,
+class ValidateMayaUnits(plugin.MayaContextPlugin,
OptionalPyblishPluginMixin):
"""Check if the Maya units are set correct"""
order = ValidateSceneOrder
label = "Maya Units"
- hosts = ['maya']
actions = [RepairContextAction]
validate_linear_units = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_arnold_attributes.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_arnold_attributes.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_arnold_attributes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_arnold_attributes.py
index e9d9a17ff4..9729c8863d 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_arnold_attributes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_arnold_attributes.py
@@ -1,23 +1,22 @@
-from maya import cmds
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api.lib import (
- maintained_selection,
- delete_after,
- undo_chunk,
- get_attribute,
- set_attribute
-)
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateMeshOrder,
- PublishValidationError
)
+from ayon_maya.api.lib import (
+ delete_after,
+ get_attribute,
+ maintained_selection,
+ set_attribute,
+ undo_chunk,
+)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateMeshArnoldAttributes(pyblish.api.InstancePlugin,
+class ValidateMeshArnoldAttributes(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate the mesh has default Arnold attributes.
@@ -30,7 +29,7 @@ class ValidateMeshArnoldAttributes(pyblish.api.InstancePlugin,
families = ["model"]
label = "Mesh Arnold Attributes"
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.SelectInvalidAction,
RepairAction
]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_empty.py
similarity index 83%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_empty.py
index c95e1ec816..12b2252eb8 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_empty.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_empty.py
@@ -1,15 +1,14 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ PublishValidationError,
RepairAction,
ValidateMeshOrder,
- PublishValidationError
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateMeshEmpty(pyblish.api.InstancePlugin):
+class ValidateMeshEmpty(plugin.MayaInstancePlugin):
"""Validate meshes have some vertices.
Its possible to have meshes without any vertices. To replicate
@@ -17,11 +16,10 @@ class ValidateMeshEmpty(pyblish.api.InstancePlugin):
"""
order = ValidateMeshOrder
- hosts = ["maya"]
families = ["model"]
label = "Mesh Empty"
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction, RepairAction
+ ayon_maya.api.action.SelectInvalidAction, RepairAction
]
@classmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_has_uv.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_has_uv.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_has_uv.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_has_uv.py
index 633fc29732..c7576d2a78 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_has_uv.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_has_uv.py
@@ -1,16 +1,15 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateMeshOrder,
+)
+from ayon_maya.api.lib import len_flattened
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-from ayon_core.hosts.maya.api.lib import len_flattened
-
-class ValidateMeshHasUVs(pyblish.api.InstancePlugin,
+class ValidateMeshHasUVs(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate the current mesh has UVs.
@@ -21,10 +20,9 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh Has UVs'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
@classmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_lamina_faces.py
similarity index 86%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_lamina_faces.py
index bfb4257f23..50e3b5b53a 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_lamina_faces.py
@@ -1,15 +1,14 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateMeshOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin,
+class ValidateMeshLaminaFaces(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate meshes don't have lamina faces.
@@ -18,10 +17,9 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh Lamina Faces'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
description = (
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_ngons.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_ngons.py
index 58d015e962..c73c8d27e8 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_ngons.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_ngons.py
@@ -1,16 +1,15 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-class ValidateMeshNgons(pyblish.api.InstancePlugin,
+class ValidateMeshNgons(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure that meshes don't have ngons
@@ -22,10 +21,9 @@ class ValidateMeshNgons(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["model"]
label = "Mesh ngons"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
description = (
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_no_negative_scale.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_no_negative_scale.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_no_negative_scale.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_no_negative_scale.py
index bf1489f92e..7a77a2a4f6 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_no_negative_scale.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_no_negative_scale.py
@@ -1,12 +1,11 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
+ ValidateMeshOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -16,7 +15,7 @@ def _as_report_list(values, prefix="- ", suffix="\n"):
return prefix + (suffix + prefix).join(values)
-class ValidateMeshNoNegativeScale(pyblish.api.InstancePlugin,
+class ValidateMeshNoNegativeScale(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure that meshes don't have a negative scale.
@@ -30,10 +29,9 @@ class ValidateMeshNoNegativeScale(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh No Negative Scale'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
@staticmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_non_manifold.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_non_manifold.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_non_manifold.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_non_manifold.py
index 958707e4f4..8288e8a3b3 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_non_manifold.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_non_manifold.py
@@ -1,13 +1,12 @@
-from maya import cmds, mel
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
+ OptionalPyblishPluginMixin,
PublishXmlValidationError,
RepairAction,
- OptionalPyblishPluginMixin
+ ValidateMeshOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds, mel
def poly_cleanup(version=4,
@@ -101,7 +100,7 @@ def _as_report_list(values, prefix="- ", suffix="\n"):
return prefix + (suffix + prefix).join(values)
-class ValidateMeshNonManifold(pyblish.api.InstancePlugin,
+class ValidateMeshNonManifold(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure that meshes don't have non-manifold edges or vertices
@@ -111,10 +110,9 @@ class ValidateMeshNonManifold(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh Non-Manifold Edges/Vertices'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
CleanupMatchingPolygons]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_non_zero_edge.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_non_zero_edge.py
index 0a8d6cf159..bd11ca6488 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_non_zero_edge.py
@@ -1,16 +1,15 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateMeshOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
-from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin,
+class ValidateMeshNonZeroEdgeLength(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate meshes don't have edges with a zero length.
@@ -23,9 +22,8 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin,
order = ValidateMeshOrder
families = ['model']
- hosts = ['maya']
label = 'Mesh Edge Length Non Zero'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
__tolerance = 1e-5
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_normals_unlocked.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_normals_unlocked.py
index 76b716d01f..dd8c523082 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_normals_unlocked.py
@@ -1,14 +1,13 @@
-from maya import cmds
+import ayon_maya.api.action
import maya.api.OpenMaya as om2
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateMeshOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
)
+from ayon_maya.api import plugin
+from maya import cmds
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -18,7 +17,7 @@ def _as_report_list(values, prefix="- ", suffix="\n"):
return prefix + (suffix + prefix).join(values)
-class ValidateMeshNormalsUnlocked(pyblish.api.InstancePlugin,
+class ValidateMeshNormalsUnlocked(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate all meshes in the instance have unlocked normals
@@ -28,10 +27,9 @@ class ValidateMeshNormalsUnlocked(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh Normals Unlocked'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_overlapping_uvs.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_overlapping_uvs.py
index a13d16e849..0ef6c2732e 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_overlapping_uvs.py
@@ -1,16 +1,15 @@
import math
-from six.moves import xrange
-from maya import cmds
+import ayon_maya.api.action
import maya.api.OpenMaya as om
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
+ ValidateMeshOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
+from six.moves import xrange
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -236,7 +235,7 @@ class GetOverlappingUVs(object):
return faces
-class ValidateMeshHasOverlappingUVs(pyblish.api.InstancePlugin,
+class ValidateMeshHasOverlappingUVs(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
""" Validate the current mesh overlapping UVs.
@@ -245,10 +244,9 @@ class ValidateMeshHasOverlappingUVs(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh Has Overlapping UVs'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
@classmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_shader_connections.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_shader_connections.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_shader_connections.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_shader_connections.py
index 70ede83f2d..e3fff157ac 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_shader_connections.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_shader_connections.py
@@ -1,13 +1,12 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateMeshOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
def pairs(iterable):
@@ -80,7 +79,7 @@ def disconnect(node_a, node_b):
cmds.disconnectAttr(source, input)
-class ValidateMeshShaderConnections(pyblish.api.InstancePlugin,
+class ValidateMeshShaderConnections(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure mesh shading engine connections are valid.
@@ -93,10 +92,9 @@ class ValidateMeshShaderConnections(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = "Mesh Shader Connections"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_single_uv_set.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_single_uv_set.py
index 21697cd903..a254cbf8a6 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_single_uv_set.py
@@ -1,17 +1,16 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateMeshOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin,
+class ValidateMeshSingleUVSet(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Warn on multiple UV sets existing for each polygon mesh.
@@ -22,11 +21,10 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model', 'pointcache']
optional = True
label = "Mesh Single UV Set"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
@staticmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_uv_set_map1.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_uv_set_map1.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_uv_set_map1.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_uv_set_map1.py
index 305a58d78e..a749edb35a 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_uv_set_map1.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_uv_set_map1.py
@@ -1,18 +1,17 @@
import inspect
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateMeshOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin,
+class ValidateMeshUVSetMap1(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate model's default set exists and is named 'map1'.
@@ -24,11 +23,10 @@ class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
optional = True
label = "Mesh has map1 UV Set"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
@classmethod
@@ -120,10 +118,10 @@ class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin,
@staticmethod
def get_description():
return inspect.cleandoc("""### Mesh found without map1 uv set
-
+
A mesh must have a default UV set named `map1` to adhere to the default
mesh behavior of Maya meshes.
-
+
There may be meshes that:
- Have no UV set
- Have no `map1` uv set but are using a different name
@@ -131,7 +129,7 @@ class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin,
#### Repair
-
+
Using repair will try to make the first UV set the `map1` uv set. If it
does not exist yet it will be created or renames the current first
UV set to `map1`.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_vertices_have_edges.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_vertices_have_edges.py
index f0962148dc..a10a275c44 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mesh_vertices_have_edges.py
@@ -1,17 +1,16 @@
-import pyblish.api
-from maya import cmds
-
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api.lib import len_flattened
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
RepairAction,
ValidateMeshOrder,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api.lib import len_flattened
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin,
+class ValidateMeshVerticesHaveEdges(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate meshes have only vertices that are connected to edges.
@@ -32,10 +31,9 @@ class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin,
"""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Mesh Vertices Have Edges'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_model_content.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_model_content.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_model_content.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_model_content.py
index bbc644c3db..5e100dca1c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_model_content.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_model_content.py
@@ -1,18 +1,17 @@
import inspect
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-class ValidateModelContent(pyblish.api.InstancePlugin,
+class ValidateModelContent(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Adheres to the content of 'model' product type
@@ -21,10 +20,9 @@ class ValidateModelContent(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["model"]
label = "Model Content"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
validate_top_group = True
optional = False
@@ -120,18 +118,18 @@ class ValidateModelContent(pyblish.api.InstancePlugin,
message="Model content is invalid. See log for more details.",
description=self.get_description()
)
-
+
@classmethod
def get_description(cls):
return inspect.cleandoc(f"""
### Model content is invalid
-
- Your model instance does not adhere to the rules of a
+
+ Your model instance does not adhere to the rules of a
model product type:
-
+
- Must have at least one visible shape in it, like a mesh.
- - Must have one root node. When exporting multiple meshes they
+ - Must have one root node. When exporting multiple meshes they
must be inside a group.
- - May only contain the following node types:
+ - May only contain the following node types:
{", ".join(cls.allowed)}
""")
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_mvlook_contents.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mvlook_contents.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_mvlook_contents.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_mvlook_contents.py
index 8c2bbf2a36..b26a3f4cb7 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_mvlook_contents.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_mvlook_contents.py
@@ -1,24 +1,24 @@
import os
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
COLOUR_SPACES = ['sRGB', 'linear', 'auto']
MIPMAP_EXTENSIONS = ['tdl']
-class ValidateMvLookContents(pyblish.api.InstancePlugin,
+class ValidateMvLookContents(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
order = ValidateContentsOrder
families = ['mvLook']
hosts = ['maya']
label = 'Validate mvLook Data'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
# Allow this validation step to be skipped when you just need to
# get things pushed through.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_no_animation.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_animation.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_no_animation.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_no_animation.py
index bf45c0e974..146dfda2ca 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_no_animation.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_animation.py
@@ -1,12 +1,11 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
+ ValidateContentsOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -16,7 +15,7 @@ def _as_report_list(values, prefix="- ", suffix="\n"):
return prefix + (suffix + prefix).join(values)
-class ValidateNoAnimation(pyblish.api.InstancePlugin,
+class ValidateNoAnimation(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure no keyframes on nodes in the Instance.
@@ -31,7 +30,7 @@ class ValidateNoAnimation(pyblish.api.InstancePlugin,
hosts = ["maya"]
families = ["model"]
optional = True
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
def process(self, instance):
if not self.is_active(instance.data):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_no_default_camera.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_default_camera.py
similarity index 85%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_no_default_camera.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_no_default_camera.py
index 3e21ec6e50..a57c02a842 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_no_default_camera.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_default_camera.py
@@ -1,12 +1,11 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
+ ValidateContentsOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -16,7 +15,7 @@ def _as_report_list(values, prefix="- ", suffix="\n"):
return prefix + (suffix + prefix).join(values)
-class ValidateNoDefaultCameras(pyblish.api.InstancePlugin,
+class ValidateNoDefaultCameras(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure no default (startup) cameras are in the instance.
@@ -26,10 +25,9 @@ class ValidateNoDefaultCameras(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['camera']
label = "No Default Cameras"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
@staticmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_no_namespace.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_namespace.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_no_namespace.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_no_namespace.py
index f546caff2c..9bd2d98e54 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_no_namespace.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_namespace.py
@@ -1,14 +1,12 @@
+import ayon_maya.api.action
import maya.cmds as cmds
-
-import pyblish.api
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
-
-import ayon_core.hosts.maya.api.action
+from ayon_maya.api import plugin
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -25,15 +23,14 @@ def get_namespace(node_name):
return node_name.rpartition(":")[0]
-class ValidateNoNamespace(pyblish.api.InstancePlugin,
+class ValidateNoNamespace(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure the nodes don't have a namespace"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['model']
label = 'No Namespaces'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_no_null_transforms.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_null_transforms.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_no_null_transforms.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_no_null_transforms.py
index 38955fd777..5c1baeb0e3 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_no_null_transforms.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_null_transforms.py
@@ -1,13 +1,12 @@
+import ayon_maya.api.action
import maya.cmds as cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -33,7 +32,7 @@ def has_shape_children(node):
return True
-class ValidateNoNullTransforms(pyblish.api.InstancePlugin,
+class ValidateNoNullTransforms(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure no null transforms are in the scene.
@@ -46,11 +45,10 @@ class ValidateNoNullTransforms(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['model']
label = 'No Empty/Null Transforms'
actions = [RepairAction,
- ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ ayon_maya.api.action.SelectInvalidAction]
optional = False
@staticmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_no_unknown_nodes.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_unknown_nodes.py
similarity index 86%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_no_unknown_nodes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_no_unknown_nodes.py
index 5cc3e95593..3662c94ba3 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_no_unknown_nodes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_unknown_nodes.py
@@ -1,12 +1,11 @@
-from maya import cmds
-
-import pyblish.api
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
+ ValidateContentsOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
def _as_report_list(values, prefix="- ", suffix="\n"):
@@ -16,7 +15,7 @@ def _as_report_list(values, prefix="- ", suffix="\n"):
return prefix + (suffix + prefix).join(values)
-class ValidateNoUnknownNodes(pyblish.api.InstancePlugin,
+class ValidateNoUnknownNodes(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Checks to see if there are any unknown nodes in the instance.
@@ -33,7 +32,7 @@ class ValidateNoUnknownNodes(pyblish.api.InstancePlugin,
families = ['model', 'rig']
optional = True
label = "Unknown Nodes"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
@staticmethod
def get_invalid(instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_no_vraymesh.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_vraymesh.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_no_vraymesh.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_no_vraymesh.py
index 2d59608e11..d14c335021 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_no_vraymesh.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_no_vraymesh.py
@@ -1,9 +1,11 @@
import pyblish.api
-from maya import cmds
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
+
def _as_report_list(values, prefix="- ", suffix="\n"):
"""Return list as bullet point list for a report"""
@@ -12,7 +14,7 @@ def _as_report_list(values, prefix="- ", suffix="\n"):
return prefix + (suffix + prefix).join(values)
-class ValidateNoVRayMesh(pyblish.api.InstancePlugin,
+class ValidateNoVRayMesh(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate there are no VRayMesh objects in the instance"""
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids.py
similarity index 85%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_node_ids.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids.py
index 2d6f231cb5..f9dafa024f 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids.py
@@ -1,14 +1,13 @@
-import pyblish.api
-
from ayon_core.pipeline.publish import (
ValidatePipelineOrder,
PublishXmlValidationError
)
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
+import ayon_maya.api.action
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class ValidateNodeIDs(pyblish.api.InstancePlugin):
+class ValidateNodeIDs(plugin.MayaInstancePlugin):
"""Validate nodes have a Colorbleed Id.
When IDs are missing from nodes *save your scene* and they should be
@@ -19,7 +18,6 @@ class ValidateNodeIDs(pyblish.api.InstancePlugin):
order = ValidatePipelineOrder
label = 'Instance Nodes Have ID'
- hosts = ['maya']
families = ["model",
"look",
"rig",
@@ -28,8 +26,8 @@ class ValidateNodeIDs(pyblish.api.InstancePlugin):
"yetiRig",
"assembly"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
- ayon_core.hosts.maya.api.action.GenerateUUIDsOnInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.GenerateUUIDsOnInvalidAction]
@classmethod
def apply_settings(cls, project_settings):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_deformed_shapes.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_deformed_shapes.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_deformed_shapes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_deformed_shapes.py
index 545ab8e28c..8e3c4e5bff 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_deformed_shapes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_deformed_shapes.py
@@ -1,13 +1,15 @@
-import pyblish.api
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ PublishValidationError,
+ RepairAction,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
-from ayon_core.pipeline.publish import (
- PublishValidationError, RepairAction, ValidateContentsOrder)
-
-class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin):
+class ValidateNodeIdsDeformedShape(plugin.MayaInstancePlugin):
"""Validate if deformed shapes have related IDs to the original shapes.
When a deformer is applied in the scene on a referenced mesh that already
@@ -19,10 +21,9 @@ class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin):
order = ValidateContentsOrder
families = ['look']
- hosts = ['maya']
label = 'Deformed shape ids'
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.SelectInvalidAction,
RepairAction
]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_in_database.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_in_database.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_in_database.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_in_database.py
index d679c510af..95e488d8e7 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_in_database.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_in_database.py
@@ -1,13 +1,14 @@
-import pyblish.api
import ayon_api
-
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- PublishValidationError, ValidatePipelineOrder)
+ PublishValidationError,
+ ValidatePipelineOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class ValidateNodeIdsInDatabase(pyblish.api.InstancePlugin):
+class ValidateNodeIdsInDatabase(plugin.MayaInstancePlugin):
"""Validate if the CB Id is related to an folder in the database
All nodes with the `cbId` attribute will be validated to ensure that
@@ -20,11 +21,10 @@ class ValidateNodeIdsInDatabase(pyblish.api.InstancePlugin):
order = ValidatePipelineOrder
label = 'Node Ids in Database'
- hosts = ['maya']
families = ["*"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
- ayon_core.hosts.maya.api.action.GenerateUUIDsOnInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.GenerateUUIDsOnInvalidAction]
@classmethod
def apply_settings(cls, project_settings):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_related.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_related.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_related.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_related.py
index 17eb58f421..7382653d35 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_related.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_related.py
@@ -1,13 +1,16 @@
import inspect
import uuid
from collections import defaultdict
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
-from ayon_core.pipeline.publish import (
- OptionalPyblishPluginMixin, PublishValidationError, ValidatePipelineOrder)
+import ayon_maya.api.action
from ayon_api import get_folders
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidatePipelineOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
def is_valid_uuid(value) -> bool:
@@ -19,20 +22,19 @@ def is_valid_uuid(value) -> bool:
return True
-class ValidateNodeIDsRelated(pyblish.api.InstancePlugin,
+class ValidateNodeIDsRelated(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate nodes have a related `cbId` to the instance.data[folderPath]"""
order = ValidatePipelineOrder
label = 'Node Ids Related (ID)'
- hosts = ['maya']
families = ["model",
"look",
"rig"]
optional = True
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
- ayon_core.hosts.maya.api.action.GenerateUUIDsOnInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.GenerateUUIDsOnInvalidAction]
@classmethod
def apply_settings(cls, project_settings):
@@ -109,14 +111,14 @@ class ValidateNodeIDsRelated(pyblish.api.InstancePlugin,
@staticmethod
def get_description():
return inspect.cleandoc("""### Node IDs must match folder id
-
+
The node ids must match the folder entity id you are publishing to.
-
- Usually these mismatch occurs if you are re-using nodes from another
- folder or project.
-
+
+ Usually these mismatch occurs if you are re-using nodes from another
+ folder or project.
+
#### How to repair?
-
- The repair action will regenerate new ids for
+
+ The repair action will regenerate new ids for
the invalid nodes to match the instance's folder.
""")
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_unique.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_unique.py
similarity index 86%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_unique.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_unique.py
index 6b44a307d2..27606baa55 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_ids_unique.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_ids_unique.py
@@ -1,17 +1,16 @@
from collections import defaultdict
-import pyblish.api
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ PublishValidationError,
ValidatePipelineOrder,
- PublishValidationError
)
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
-
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-class ValidateNodeIdsUnique(pyblish.api.InstancePlugin):
+class ValidateNodeIdsUnique(plugin.MayaInstancePlugin):
"""Validate the nodes in the instance have a unique Colorbleed Id
Here we ensure that what has been added to the instance is unique
@@ -19,14 +18,13 @@ class ValidateNodeIdsUnique(pyblish.api.InstancePlugin):
order = ValidatePipelineOrder
label = 'Non Duplicate Instance Members (ID)'
- hosts = ['maya']
families = ["model",
"look",
"rig",
"yetiRig"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
- ayon_core.hosts.maya.api.action.GenerateUUIDsOnInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.GenerateUUIDsOnInvalidAction]
@classmethod
def apply_settings(cls, project_settings):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_no_ghosting.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_node_no_ghosting.py
index 10cbbc9a88..184e4eaf15 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_node_no_ghosting.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_node_no_ghosting.py
@@ -1,16 +1,14 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-
-class ValidateNodeNoGhosting(pyblish.api.InstancePlugin,
+class ValidateNodeNoGhosting(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure nodes do not have ghosting enabled.
@@ -24,10 +22,9 @@ class ValidateNodeNoGhosting(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['model', 'rig']
label = "No Ghosting"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
_attributes = {'ghosting': 0}
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_plugin_path_attributes.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_plugin_path_attributes.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_plugin_path_attributes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_plugin_path_attributes.py
index f961ec6e4a..506759516e 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_plugin_path_attributes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_plugin_path_attributes.py
@@ -1,26 +1,23 @@
import os
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api.action import SelectInvalidAction
+from ayon_maya.api.lib import pairwise
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.hosts.maya.api.lib import pairwise
-from ayon_core.hosts.maya.api.action import SelectInvalidAction
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-
-class ValidatePluginPathAttributes(pyblish.api.InstancePlugin,
+class ValidatePluginPathAttributes(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""
Validate plug-in path attributes point to existing file paths.
"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ["workfile"]
label = "Plug-in Path Attributes"
actions = [SelectInvalidAction]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_render_image_rule.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_render_image_rule.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_render_image_rule.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_render_image_rule.py
index 117f7df822..fd1a9ad9e4 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_render_image_rule.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_render_image_rule.py
@@ -1,18 +1,16 @@
import os
-import pyblish.api
-
-from maya import cmds
-
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
RepairAction,
ValidateContentsOrder,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateRenderImageRule(pyblish.api.InstancePlugin,
+class ValidateRenderImageRule(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates Maya Workpace "images" file rule matches project settings.
@@ -24,7 +22,6 @@ class ValidateRenderImageRule(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
label = "Images File Rule (Workspace)"
- hosts = ["maya"]
families = ["renderlayer"]
actions = [RepairAction]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_render_no_default_cameras.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_render_no_default_cameras.py
similarity index 82%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_render_no_default_cameras.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_render_no_default_cameras.py
index 41c0fa4807..083b21b819 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_render_no_default_cameras.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_render_no_default_cameras.py
@@ -1,24 +1,21 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-
-class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin,
+class ValidateRenderNoDefaultCameras(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure no default (startup) cameras are to be rendered."""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['renderlayer']
label = "No Default Cameras Renderable"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
@staticmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_render_single_camera.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_render_single_camera.py
similarity index 87%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_render_single_camera.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_render_single_camera.py
index e186d74b89..6856b9a0f1 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_render_single_camera.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_render_single_camera.py
@@ -1,19 +1,18 @@
-import re
import inspect
+import re
-import pyblish.api
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api.lib_rendersettings import RenderSettings
+from ayon_maya.api import plugin
from maya import cmds
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api.lib_rendersettings import RenderSettings
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-class ValidateRenderSingleCamera(pyblish.api.InstancePlugin,
+class ValidateRenderSingleCamera(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate renderable camera count for layer and token.
@@ -23,10 +22,9 @@ class ValidateRenderSingleCamera(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
label = "Render Single Camera"
- hosts = ['maya']
families = ["renderlayer",
"vrayscene"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
R_CAMERA_TOKEN = re.compile(r'%c|', re.IGNORECASE)
@@ -77,8 +75,8 @@ class ValidateRenderSingleCamera(pyblish.api.InstancePlugin,
Your render cameras are misconfigured. You may have no render
camera set or have multiple cameras with a render filename
prefix that does not include the `` token.
-
+
See the logs for more details about the cameras.
-
+
"""
)
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_renderlayer_aovs.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_renderlayer_aovs.py
index 910e336fc1..16350cfa08 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_renderlayer_aovs.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_renderlayer_aovs.py
@@ -1,13 +1,14 @@
import ayon_api
+import ayon_maya.api.action
import pyblish.api
-
-import ayon_core.hosts.maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin,
+
+class ValidateRenderLayerAOVs(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate created AOVs / RenderElement is registered in the database
@@ -26,9 +27,8 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin,
order = pyblish.api.ValidatorOrder + 0.1
label = "Render Passes / AOVs Are Registered"
- hosts = ["maya"]
families = ["renderlayer"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rendersettings.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rendersettings.py
similarity index 98%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_rendersettings.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_rendersettings.py
index 7badfdc027..7b21bf6a14 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_rendersettings.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rendersettings.py
@@ -3,17 +3,16 @@
import re
from collections import OrderedDict
-from maya import cmds, mel
-
-import pyblish.api
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
-from ayon_core.hosts.maya.api import lib
-from ayon_core.hosts.maya.api.lib_rendersettings import RenderSettings
+from ayon_maya.api import lib
+from ayon_maya.api.lib_rendersettings import RenderSettings
+from ayon_maya.api import plugin
+from maya import cmds, mel
def convert_to_int_or_float(string_value):
@@ -38,7 +37,7 @@ def get_redshift_image_format_labels():
return mel.eval("{0}={0}".format(var))
-class ValidateRenderSettings(pyblish.api.InstancePlugin,
+class ValidateRenderSettings(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates the global render settings
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_resolution.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_resolution.py
index d822dca288..58d2ad3030 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_resolution.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_resolution.py
@@ -1,21 +1,21 @@
import pyblish.api
from ayon_core.pipeline import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
)
-from maya import cmds
from ayon_core.pipeline.publish import RepairAction
-from ayon_core.hosts.maya.api import lib
-from ayon_core.hosts.maya.api.lib import reset_scene_resolution
+from ayon_maya.api import lib
+from ayon_maya.api.lib import reset_scene_resolution
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateResolution(pyblish.api.InstancePlugin,
+class ValidateResolution(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate the render resolution setting aligned with DB"""
order = pyblish.api.ValidatorOrder
families = ["renderlayer"]
- hosts = ["maya"]
label = "Validate Resolution"
actions = [RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_resources.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_resources.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_resources.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_resources.py
index 725e86450d..0ec51d909c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_resources.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_resources.py
@@ -1,14 +1,14 @@
import os
from collections import defaultdict
-import pyblish.api
from ayon_core.pipeline.publish import (
+ PublishValidationError,
ValidateContentsOrder,
- PublishValidationError
)
+from ayon_maya.api import plugin
-class ValidateResources(pyblish.api.InstancePlugin):
+class ValidateResources(plugin.MayaInstancePlugin):
"""Validates mapped resources.
These are external files to the current application, for example
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_review.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_review.py
similarity index 91%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_review.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_review.py
index fcfd851368..76f61dc3e5 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_review.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_review.py
@@ -1,11 +1,10 @@
-import pyblish.api
-
from ayon_core.pipeline.publish import (
ValidateContentsOrder, PublishValidationError
)
+from ayon_maya.api import plugin
-class ValidateReview(pyblish.api.InstancePlugin):
+class ValidateReview(plugin.MayaInstancePlugin):
"""Validate review."""
order = ValidateContentsOrder
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_contents.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_contents.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_rig_contents.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_contents.py
index f05e2b7311..ada2ce045b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_contents.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_contents.py
@@ -1,14 +1,14 @@
-import pyblish.api
-from maya import cmds
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
ValidateContentsOrder,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateRigContents(pyblish.api.InstancePlugin,
+class ValidateRigContents(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure rig contains pipeline-critical content
@@ -20,9 +20,8 @@ class ValidateRigContents(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
label = "Rig Contents"
- hosts = ["maya"]
families = ["rig"]
- action = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ action = [ayon_maya.api.action.SelectInvalidAction]
optional = True
accepted_output = ["mesh", "transform"]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_controllers.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_controllers.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_rig_controllers.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_controllers.py
index 814ff50177..fed78df2da 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_controllers.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_controllers.py
@@ -1,18 +1,16 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ RepairAction,
+ ValidateContentsOrder,
+)
+from ayon_maya.api.lib import undo_chunk
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- RepairAction,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api.lib import undo_chunk
-
-
-class ValidateRigControllers(pyblish.api.InstancePlugin,
+class ValidateRigControllers(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate rig controllers.
@@ -33,11 +31,10 @@ class ValidateRigControllers(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder + 0.05
label = "Rig Controllers"
- hosts = ["maya"]
families = ["rig"]
optional = True
actions = [RepairAction,
- ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ ayon_maya.api.action.SelectInvalidAction]
# Default controller values
CONTROLLER_DEFAULTS = {
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_controllers_arnold_attributes.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_controllers_arnold_attributes.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_rig_controllers_arnold_attributes.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_controllers_arnold_attributes.py
index ea2de81036..06691f4bd3 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_controllers_arnold_attributes.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_controllers_arnold_attributes.py
@@ -1,19 +1,16 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ RepairAction,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- RepairAction,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-from ayon_core.hosts.maya.api import lib
-import ayon_core.hosts.maya.api.action
-
-
-class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin,
+class ValidateRigControllersArnoldAttributes(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate rig control curves have no keyable arnold attributes.
@@ -35,11 +32,10 @@ class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder + 0.05
label = "Rig Controllers (Arnold Attributes)"
- hosts = ["maya"]
families = ["rig"]
optional = False
actions = [RepairAction,
- ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ ayon_maya.api.action.SelectInvalidAction]
attributes = [
"rcurve",
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_joints_hidden.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_joints_hidden.py
similarity index 81%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_rig_joints_hidden.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_joints_hidden.py
index 78cc3f5938..f0593ed7bc 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_joints_hidden.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_joints_hidden.py
@@ -1,18 +1,16 @@
-from maya import cmds
-
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateRigJointsHidden(pyblish.api.InstancePlugin,
+class ValidateRigJointsHidden(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate all joints are hidden visually.
@@ -25,10 +23,9 @@ class ValidateRigJointsHidden(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['rig']
label = "Joints Hidden"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_out_set_node_ids.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_out_set_node_ids.py
index d94ddc5f2a..e5f7a5b204 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_out_set_node_ids.py
@@ -1,20 +1,18 @@
+import ayon_maya.api.action
import maya.cmds as cmds
-
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishXmlValidationError,
RepairAction,
ValidateContentsOrder,
- PublishXmlValidationError,
- OptionalPyblishPluginMixin,
+ apply_plugin_settings_automatically,
get_plugin_settings,
- apply_plugin_settings_automatically
)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
-class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin,
+class ValidateRigOutSetNodeIds(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate if deformed shapes have related IDs to the original shapes.
@@ -27,10 +25,9 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
families = ["rig"]
- hosts = ['maya']
label = 'Rig Out Set Node Ids'
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.SelectInvalidAction,
RepairAction
]
allow_history_only = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_output_ids.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_output_ids.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_rig_output_ids.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_output_ids.py
index d04006f013..064c83c0b9 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_rig_output_ids.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_rig_output_ids.py
@@ -1,16 +1,14 @@
from collections import defaultdict
-from maya import cmds
-
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api.lib import get_id, set_id
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError
)
+from ayon_maya.api.lib import get_id, set_id
+from ayon_maya.api import plugin
+from maya import cmds
def get_basename(node):
@@ -18,7 +16,7 @@ def get_basename(node):
return node.rsplit("|", 1)[-1].rsplit(":", 1)[-1]
-class ValidateRigOutputIds(pyblish.api.InstancePlugin):
+class ValidateRigOutputIds(plugin.MayaInstancePlugin):
"""Validate rig output ids.
Ids must share the same id as similarly named nodes in the scene. This is
@@ -27,10 +25,9 @@ class ValidateRigOutputIds(pyblish.api.InstancePlugin):
"""
order = ValidateContentsOrder + 0.05
label = "Rig Output Ids"
- hosts = ["maya"]
families = ["rig"]
actions = [RepairAction,
- ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ ayon_maya.api.action.SelectInvalidAction]
@classmethod
def apply_settings(cls, project_settings):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_scene_set_workspace.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_scene_set_workspace.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_scene_set_workspace.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_scene_set_workspace.py
index c7d5de2050..cd36f98dfb 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_scene_set_workspace.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_scene_set_workspace.py
@@ -1,10 +1,11 @@
import os
import maya.cmds as cmds
-import pyblish.api
-
from ayon_core.pipeline.publish import (
- PublishValidationError, ValidatePipelineOrder)
+ PublishValidationError,
+ ValidatePipelineOrder,
+)
+from ayon_maya.api import plugin
def is_subdir(path, root_dir):
@@ -26,11 +27,10 @@ def is_subdir(path, root_dir):
return True
-class ValidateSceneSetWorkspace(pyblish.api.ContextPlugin):
+class ValidateSceneSetWorkspace(plugin.MayaContextPlugin):
"""Validate the scene is inside the currently set Maya workspace"""
order = ValidatePipelineOrder
- hosts = ['maya']
label = 'Maya Workspace Set'
def process(self, context):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_setdress_root.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_setdress_root.py
similarity index 83%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_setdress_root.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_setdress_root.py
index f88e33fdfb..26245435eb 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_setdress_root.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_setdress_root.py
@@ -1,16 +1,15 @@
-import pyblish.api
from ayon_core.pipeline.publish import (
+ PublishValidationError,
ValidateContentsOrder,
- PublishValidationError
)
+from ayon_maya.api import plugin
-class ValidateSetdressRoot(pyblish.api.InstancePlugin):
+class ValidateSetdressRoot(plugin.MayaInstancePlugin):
"""Validate if set dress top root node is published."""
order = ValidateContentsOrder
label = "SetDress Root"
- hosts = ["maya"]
families = ["setdress"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shader_name.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_shader_name.py
index 09c17202c5..45d7ee1c8b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_shader_name.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shader_name.py
@@ -1,14 +1,16 @@
import re
-import pyblish.api
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- OptionalPyblishPluginMixin, PublishValidationError, ValidateContentsOrder)
-
-class ValidateShaderName(pyblish.api.InstancePlugin,
+class ValidateShaderName(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate shader name assigned.
@@ -18,9 +20,8 @@ class ValidateShaderName(pyblish.api.InstancePlugin,
optional = True
order = ValidateContentsOrder
families = ["look"]
- hosts = ['maya']
label = 'Validate Shaders Name'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
regex = r'(?P.*)_(.*)_SHD'
# The default connections to check
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_default_names.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_default_names.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_shape_default_names.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_default_names.py
index c4c4c909d3..4e4f98d755 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_default_names.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_default_names.py
@@ -1,23 +1,21 @@
import re
-from maya import cmds
-
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- RepairAction,
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
+ RepairAction,
+ ValidateContentsOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
def short_name(node):
return node.rsplit("|", 1)[-1].rsplit(":", 1)[-1]
-class ValidateShapeDefaultNames(pyblish.api.InstancePlugin,
+class ValidateShapeDefaultNames(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates that Shape names are using Maya's default format.
@@ -43,7 +41,7 @@ class ValidateShapeDefaultNames(pyblish.api.InstancePlugin,
families = ['model']
optional = True
label = "Shape Default Naming"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
@staticmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_render_stats.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_render_stats.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_shape_render_stats.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_render_stats.py
index 52ce3c5436..a9c3e861f0 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_render_stats.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_render_stats.py
@@ -1,25 +1,22 @@
-import pyblish.api
-
-from maya import cmds
-
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairAction,
ValidateMeshOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateShapeRenderStats(pyblish.api.InstancePlugin,
+class ValidateShapeRenderStats(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Ensure all render stats are set to the default values."""
order = ValidateMeshOrder
- hosts = ['maya']
families = ['model']
label = 'Shape Default Render Stats'
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ actions = [ayon_maya.api.action.SelectInvalidAction,
RepairAction]
defaults = {'castsShadows': 1,
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_zero.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_zero.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_shape_zero.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_zero.py
index 6c89258085..8c11009374 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_shape_zero.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_shape_zero.py
@@ -1,18 +1,16 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ RepairAction,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.hosts.maya.api import lib
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- RepairAction,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-
-class ValidateShapeZero(pyblish.api.InstancePlugin,
+class ValidateShapeZero(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Shape components may not have any "tweak" values
@@ -21,11 +19,10 @@ class ValidateShapeZero(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["model"]
label = "Shape Zero (Freeze)"
actions = [
- ayon_core.hosts.maya.api.action.SelectInvalidAction,
+ ayon_maya.api.action.SelectInvalidAction,
RepairAction
]
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_single_assembly.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_single_assembly.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_single_assembly.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_single_assembly.py
index f5d73553d3..0a760e2acc 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_single_assembly.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_single_assembly.py
@@ -1,11 +1,11 @@
-import pyblish.api
from ayon_core.pipeline.publish import (
+ PublishValidationError,
ValidateContentsOrder,
- PublishValidationError
)
+from ayon_maya.api import plugin
-class ValidateSingleAssembly(pyblish.api.InstancePlugin):
+class ValidateSingleAssembly(plugin.MayaInstancePlugin):
"""Ensure the content of the instance is grouped in a single hierarchy
The instance must have a single root node containing all the content.
@@ -21,7 +21,6 @@ class ValidateSingleAssembly(pyblish.api.InstancePlugin):
"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['rig']
label = 'Single Assembly'
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_skeletalmesh_hierarchy.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skeletalmesh_hierarchy.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_skeletalmesh_hierarchy.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_skeletalmesh_hierarchy.py
index 172453f1ef..9904fdce22 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_skeletalmesh_hierarchy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skeletalmesh_hierarchy.py
@@ -1,21 +1,19 @@
# -*- coding: utf-8 -*-
-import pyblish.api
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
+ OptionalPyblishPluginMixin,
PublishXmlValidationError,
- OptionalPyblishPluginMixin
+ ValidateContentsOrder,
)
-
+from ayon_maya.api import plugin
from maya import cmds
-class ValidateSkeletalMeshHierarchy(pyblish.api.InstancePlugin,
+class ValidateSkeletalMeshHierarchy(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates that nodes has common root."""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["skeletalMesh"]
label = "Skeletal Mesh Top Node"
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_skeletalmesh_triangulated.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skeletalmesh_triangulated.py
similarity index 88%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_skeletalmesh_triangulated.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_skeletalmesh_triangulated.py
index cc25e769e6..7b2b005e9c 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_skeletalmesh_triangulated.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skeletalmesh_triangulated.py
@@ -1,24 +1,19 @@
# -*- coding: utf-8 -*-
-import pyblish.api
-from ayon_core.hosts.maya.api.action import (
- SelectInvalidAction,
-)
from ayon_core.pipeline.publish import (
+ PublishValidationError,
RepairAction,
ValidateContentsOrder,
- PublishValidationError
)
-
-
+from ayon_maya.api.action import SelectInvalidAction
+from ayon_maya.api import plugin
from maya import cmds
-class ValidateSkeletalMeshTriangulated(pyblish.api.InstancePlugin):
+class ValidateSkeletalMeshTriangulated(plugin.MayaInstancePlugin):
"""Validates that the geometry has been triangulated."""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["skeletalMesh"]
label = "Skeletal Mesh Triangulated"
optional = True
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_skeleton_top_group_hierarchy.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skeleton_top_group_hierarchy.py
similarity index 90%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_skeleton_top_group_hierarchy.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_skeleton_top_group_hierarchy.py
index 9fbe0f440b..4e0dc38a36 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_skeleton_top_group_hierarchy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skeleton_top_group_hierarchy.py
@@ -1,17 +1,15 @@
# -*- coding: utf-8 -*-
"""Plugin for validating naming conventions."""
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-
-class ValidateSkeletonTopGroupHierarchy(pyblish.api.InstancePlugin,
+class ValidateSkeletonTopGroupHierarchy(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates top group hierarchy in the SETs
Make sure the object inside the SETs are always top
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skinCluster_deformer_set.py
similarity index 92%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_skinCluster_deformer_set.py
index a548e12f33..32015a57bc 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_skinCluster_deformer_set.py
@@ -1,16 +1,15 @@
from maya import cmds
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
OptionalPyblishPluginMixin,
PublishValidationError
)
+from ayon_maya.api import plugin
-class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin,
+class ValidateSkinclusterDeformerSet(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate skinClusters on meshes have valid member relationships.
@@ -21,10 +20,9 @@ class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ['maya']
families = ['fbx']
label = "Skincluster Deformer Relationships"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_step_size.py
similarity index 86%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_step_size.py
index a276a5b644..303885d907 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_step_size.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_step_size.py
@@ -1,14 +1,13 @@
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
ValidateContentsOrder,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateStepSize(pyblish.api.InstancePlugin,
+class ValidateStepSize(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates the step size for the instance is in a valid range.
@@ -21,7 +20,7 @@ class ValidateStepSize(pyblish.api.InstancePlugin,
families = ['camera',
'pointcache',
'animation']
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
MIN = 0.01
MAX = 1.0
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_transform_naming_suffix.py
similarity index 94%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_transform_naming_suffix.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_transform_naming_suffix.py
index e565866778..3a60bc7aef 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_transform_naming_suffix.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_transform_naming_suffix.py
@@ -1,19 +1,18 @@
# -*- coding: utf-8 -*-
"""Plugin for validating naming conventions."""
import json
+
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-
-class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin,
+class ValidateTransformNamingSuffix(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates transform suffix based on the type of its children shapes.
@@ -36,11 +35,10 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["model"]
optional = True
label = "Suffix Naming Conventions"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
SUFFIX_NAMING_TABLE = json.dumps({
"mesh": ["_GEO", "_GES", "_GEP", "_OSD"],
"nurbsCurve": ["_CRV"],
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_transform_zero.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_transform_zero.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_transform_zero.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_transform_zero.py
index cd96ebb10d..e251572c0d 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_transform_zero.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_transform_zero.py
@@ -1,17 +1,16 @@
import inspect
-from maya import cmds
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
+ ValidateContentsOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateTransformZero(pyblish.api.InstancePlugin,
+class ValidateTransformZero(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Transforms can't have any values
@@ -22,10 +21,9 @@ class ValidateTransformZero(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["model"]
label = "Transform Zero (Freeze)"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
_identity = [1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
@@ -86,7 +84,7 @@ class ValidateTransformZero(pyblish.api.InstancePlugin,
def get_description():
return inspect.cleandoc("""### Transform can't have any values
- The model publish allows no transformations.
+ The model publish allows no transformations.
You must **freeze transformations** to continue.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unique_names.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unique_names.py
similarity index 81%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_unique_names.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_unique_names.py
index 0066d70531..cbe5d2f647 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_unique_names.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unique_names.py
@@ -1,15 +1,14 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-class ValidateUniqueNames(pyblish.api.InstancePlugin,
+class ValidateUniqueNames(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""transform names should be unique
@@ -18,10 +17,9 @@ class ValidateUniqueNames(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["model"]
label = "Unique transform name"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = True
@staticmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_mesh_triangulated.py
similarity index 80%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_mesh_triangulated.py
index 6440c00eae..f283150892 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_mesh_triangulated.py
@@ -1,25 +1,22 @@
# -*- coding: utf-8 -*-
-
-from maya import cmds
-import pyblish.api
-
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateMeshOrder,
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
+ ValidateMeshOrder,
)
-import ayon_core.hosts.maya.api.action
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateUnrealMeshTriangulated(pyblish.api.InstancePlugin,
+class ValidateUnrealMeshTriangulated(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate if mesh is made of triangles for Unreal Engine"""
order = ValidateMeshOrder
- hosts = ["maya"]
families = ["staticMesh"]
label = "Mesh is Triangulated"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
active = False
@classmethod
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_staticmesh_naming.py
similarity index 95%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_staticmesh_naming.py
index 88b0ff0e71..dee6563989 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_staticmesh_naming.py
@@ -2,17 +2,16 @@
"""Validator for correct naming of Static Meshes."""
import re
-import pyblish.api
-
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
+ ValidateContentsOrder,
)
+from ayon_maya.api import plugin
-class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin,
+class ValidateUnrealStaticMeshName(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate name of Unreal Static Mesh
@@ -55,10 +54,9 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin,
"""
optional = True
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["staticMesh"]
label = "Unreal Static Mesh Name"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
regex_mesh = r"(?P.*))"
regex_collision = r"(?P.*)"
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_up_axis.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_up_axis.py
similarity index 86%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_unreal_up_axis.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_up_axis.py
index f7acd41cea..d970eb8020 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_unreal_up_axis.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_unreal_up_axis.py
@@ -1,24 +1,21 @@
# -*- coding: utf-8 -*-
-
-from maya import cmds
-import pyblish.api
-
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- RepairAction,
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
+ RepairAction,
+ ValidateContentsOrder,
)
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateUnrealUpAxis(pyblish.api.ContextPlugin,
+class ValidateUnrealUpAxis(plugin.MayaContextPlugin,
OptionalPyblishPluginMixin):
"""Validate if Z is set as up axis in Maya"""
optional = True
active = False
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["staticMesh"]
label = "Unreal Up-Axis check"
actions = [RepairAction]
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_visible_only.py
similarity index 84%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_visible_only.py
index 1fdb476dba..b694b890fc 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_visible_only.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_visible_only.py
@@ -1,15 +1,14 @@
-import pyblish.api
-
-from ayon_core.hosts.maya.api.lib import iter_visible_nodes_in_range
-import ayon_core.hosts.maya.api.action
+import ayon_maya.api.action
from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
+ ValidateContentsOrder,
)
+from ayon_maya.api.lib import iter_visible_nodes_in_range
+from ayon_maya.api import plugin
-class ValidateAlembicVisibleOnly(pyblish.api.InstancePlugin,
+class ValidateAlembicVisibleOnly(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validates at least a single node is visible in frame range.
@@ -19,9 +18,8 @@ class ValidateAlembicVisibleOnly(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder + 0.05
label = "Alembic Visible Only"
- hosts = ["maya"]
families = ["pointcache", "animation"]
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vray.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray.py
similarity index 84%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_vray.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_vray.py
index db78212658..7cf064f993 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_vray.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray.py
@@ -1,15 +1,14 @@
-from maya import cmds
-
import pyblish.api
from ayon_core.pipeline.publish import PublishValidationError
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateVray(pyblish.api.InstancePlugin):
+class ValidateVray(plugin.MayaInstancePlugin):
"""Validate general Vray setup."""
order = pyblish.api.ValidatorOrder
label = 'VRay'
- hosts = ["maya"]
families = ["vrayproxy"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_distributed_rendering.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_distributed_rendering.py
index b3978b8483..0338798e3f 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_distributed_rendering.py
@@ -1,17 +1,16 @@
-import pyblish.api
-from maya import cmds
-
-from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline.publish import (
KnownPublishError,
+ OptionalPyblishPluginMixin,
PublishValidationError,
RepairAction,
ValidateContentsOrder,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import lib
+from ayon_maya.api import plugin
+from maya import cmds
-class ValidateVRayDistributedRendering(pyblish.api.InstancePlugin,
+class ValidateVRayDistributedRendering(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate V-Ray Distributed Rendering is ignored in batch mode.
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_referenced_aovs.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_referenced_aovs.py
index 9df5fb8488..a330866e9b 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_referenced_aovs.py
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
"""Validate if there are AOVs pulled from references."""
-import pyblish.api
import types
+
+import pyblish.api
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ RepairContextAction,
+)
+from ayon_maya.api import plugin
from maya import cmds
-from ayon_core.pipeline.publish import (
- RepairContextAction,
- OptionalPyblishPluginMixin,
- PublishValidationError
-)
-
-class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin,
+class ValidateVrayReferencedAOVs(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate whether the V-Ray Render Elements (AOVs) include references.
@@ -23,7 +24,6 @@ class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin,
order = pyblish.api.ValidatorOrder
label = 'VRay Referenced AOVs'
- hosts = ['maya']
families = ['renderlayer']
actions = [RepairContextAction]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_translator_settings.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_translator_settings.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_vray_translator_settings.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_translator_settings.py
index a3d93dd9c0..a4b34db2a1 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_vray_translator_settings.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vray_translator_settings.py
@@ -1,18 +1,17 @@
# -*- coding: utf-8 -*-
"""Validate VRay Translator settings."""
-import pyblish.api
from ayon_core.pipeline.publish import (
- context_plugin_should_run,
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
RepairContextAction,
ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
+ context_plugin_should_run,
)
-
+from ayon_maya.api import plugin
from maya import cmds
-class ValidateVRayTranslatorEnabled(pyblish.api.ContextPlugin,
+class ValidateVRayTranslatorEnabled(plugin.MayaContextPlugin,
OptionalPyblishPluginMixin):
"""Validate VRay Translator settings for extracting vrscenes."""
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vrayproxy.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_vrayproxy.py
index 0288d4b865..67d656b183 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vrayproxy.py
@@ -1,17 +1,16 @@
import pyblish.api
-
from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin,
- PublishValidationError
+ PublishValidationError,
)
+from ayon_maya.api import plugin
-class ValidateVrayProxy(pyblish.api.InstancePlugin,
+class ValidateVrayProxy(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
order = pyblish.api.ValidatorOrder
label = "VRay Proxy Settings"
- hosts = ["maya"]
families = ["vrayproxy"]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy_members.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vrayproxy_members.py
similarity index 82%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy_members.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_vrayproxy_members.py
index 6732d09202..cfb17cda55 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_vrayproxy_members.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_vrayproxy_members.py
@@ -1,24 +1,21 @@
+import ayon_maya.api.action
import pyblish.api
-
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-
-class ValidateVrayProxyMembers(pyblish.api.InstancePlugin,
+class ValidateVrayProxyMembers(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate whether the V-Ray Proxy instance has shape members"""
order = pyblish.api.ValidatorOrder
label = 'VRay Proxy Members'
- hosts = ['maya']
families = ['vrayproxy']
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_xgen.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_xgen.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_xgen.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_xgen.py
index 7e0f01c482..050165db01 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_xgen.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_xgen.py
@@ -1,18 +1,17 @@
import json
import maya.cmds as cmds
-import xgenm
-
import pyblish.api
+import xgenm
from ayon_core.pipeline.publish import PublishValidationError
+from ayon_maya.api import plugin
-class ValidateXgen(pyblish.api.InstancePlugin):
+class ValidateXgen(plugin.MayaInstancePlugin):
"""Validate Xgen data."""
label = "Validate Xgen"
order = pyblish.api.ValidatorOrder
- host = ["maya"]
families = ["xgen"]
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_renderscript_callbacks.py
similarity index 96%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_renderscript_callbacks.py
index 086cb7b1f5..ce6410deaa 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_renderscript_callbacks.py
@@ -1,14 +1,13 @@
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin,
+class ValidateYetiRenderScriptCallbacks(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Check if the render script callbacks will be used during the rendering
@@ -27,7 +26,6 @@ class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin,
order = ValidateContentsOrder
label = "Yeti Render Script Callbacks"
- hosts = ["maya"]
families = ["renderlayer"]
optional = False
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_cache_state.py
similarity index 89%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_cache_state.py
index 84614fc0be..a7f272c1ec 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_cache_state.py
@@ -1,16 +1,17 @@
import inspect
-import pyblish.api
+import ayon_maya.api.action
import maya.cmds as cmds
-import ayon_core.hosts.maya.api.action
+import pyblish.api
from ayon_core.pipeline.publish import (
- RepairAction,
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
+ RepairAction,
)
+from ayon_maya.api import plugin
-class ValidateYetiRigCacheState(pyblish.api.InstancePlugin,
+class ValidateYetiRigCacheState(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate the I/O attributes of the node
@@ -22,10 +23,9 @@ class ValidateYetiRigCacheState(pyblish.api.InstancePlugin,
order = pyblish.api.ValidatorOrder
label = "Yeti Rig Cache State"
- hosts = ["maya"]
families = ["yetiRig"]
actions = [RepairAction,
- ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ ayon_maya.api.action.SelectInvalidAction]
optional = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_input_in_instance.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_input_in_instance.py
similarity index 85%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_input_in_instance.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_input_in_instance.py
index 77e189e37b..8c258b4455 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_input_in_instance.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_input_in_instance.py
@@ -1,24 +1,21 @@
+import ayon_maya.api.action
+from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
+ PublishValidationError,
+ ValidateContentsOrder,
+)
+from ayon_maya.api import plugin
from maya import cmds
-import pyblish.api
-import ayon_core.hosts.maya.api.action
-from ayon_core.pipeline.publish import (
- ValidateContentsOrder,
- PublishValidationError,
- OptionalPyblishPluginMixin
-)
-
-
-class ValidateYetiRigInputShapesInInstance(pyblish.api.InstancePlugin,
+class ValidateYetiRigInputShapesInInstance(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate if all input nodes are part of the instance's hierarchy"""
order = ValidateContentsOrder
- hosts = ["maya"]
families = ["yetiRig"]
label = "Yeti Rig Input Shapes In Instance"
- actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
+ actions = [ayon_maya.api.action.SelectInvalidAction]
optional = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_settings.py b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_settings.py
similarity index 93%
rename from client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_settings.py
rename to server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_settings.py
index 6bd2ebb753..fa0836e0ef 100644
--- a/client/ayon_core/hosts/maya/plugins/publish/validate_yeti_rig_settings.py
+++ b/server_addon/maya/client/ayon_maya/plugins/publish/validate_yeti_rig_settings.py
@@ -1,12 +1,12 @@
import pyblish.api
-
from ayon_core.pipeline.publish import (
+ OptionalPyblishPluginMixin,
PublishValidationError,
- OptionalPyblishPluginMixin
)
+from ayon_maya.api import plugin
-class ValidateYetiRigSettings(pyblish.api.InstancePlugin,
+class ValidateYetiRigSettings(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Validate Yeti Rig Settings have collected input connections.
diff --git a/client/ayon_core/hosts/maya/plugins/workfile_build/assign_look_placeholder.py b/server_addon/maya/client/ayon_maya/plugins/workfile_build/assign_look_placeholder.py
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/workfile_build/assign_look_placeholder.py
rename to server_addon/maya/client/ayon_maya/plugins/workfile_build/assign_look_placeholder.py
diff --git a/client/ayon_core/hosts/maya/plugins/workfile_build/load_placeholder.py b/server_addon/maya/client/ayon_maya/plugins/workfile_build/load_placeholder.py
similarity index 97%
rename from client/ayon_core/hosts/maya/plugins/workfile_build/load_placeholder.py
rename to server_addon/maya/client/ayon_maya/plugins/workfile_build/load_placeholder.py
index b07c7e9a70..6cf38e591a 100644
--- a/client/ayon_core/hosts/maya/plugins/workfile_build/load_placeholder.py
+++ b/server_addon/maya/client/ayon_maya/plugins/workfile_build/load_placeholder.py
@@ -4,12 +4,12 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
PlaceholderLoadMixin,
LoadPlaceholderItem
)
-from ayon_core.hosts.maya.api.lib import (
+from ayon_maya.api.lib import (
get_container_transforms,
get_node_parent,
get_node_index_under_parent
)
-from ayon_core.hosts.maya.api.workfile_template_builder import (
+from ayon_maya.api.workfile_template_builder import (
MayaPlaceholderPlugin,
)
diff --git a/server_addon/maya/client/ayon_maya/plugins/workfile_build/script_placeholder.py b/server_addon/maya/client/ayon_maya/plugins/workfile_build/script_placeholder.py
new file mode 100644
index 0000000000..ff54306220
--- /dev/null
+++ b/server_addon/maya/client/ayon_maya/plugins/workfile_build/script_placeholder.py
@@ -0,0 +1,201 @@
+from maya import cmds
+
+from ayon_maya.api.workfile_template_builder import (
+ MayaPlaceholderPlugin
+)
+from ayon_core.lib import NumberDef, TextDef, EnumDef
+from ayon_core.lib.events import weakref_partial
+
+
+EXAMPLE_SCRIPT = """
+# Access maya commands
+from maya import cmds
+
+# Access the placeholder node
+placeholder_node = placeholder.scene_identifier
+
+# Access the event callback
+if event is None:
+ print(f"Populating {placeholder}")
+else:
+ if event.topic == "template.depth_processed":
+ print(f"Processed depth: {event.get('depth')}")
+ elif event.topic == "template.finished":
+ print("Build finished.")
+""".strip()
+
+
+class MayaPlaceholderScriptPlugin(MayaPlaceholderPlugin):
+ """Execute a script at the given `order` during workfile build.
+
+ This is a very low-level placeholder to run Python scripts at a given
+ point in time during the workfile template build.
+
+ It can create either a locator or an objectSet as placeholder node.
+ It defaults to an objectSet, since allowing to run on e.g. other
+ placeholder node members can be useful, e.g. using:
+
+ >>> members = cmds.sets(placeholder.scene_identifier, query=True)
+
+ """
+
+ identifier = "maya.runscript"
+ label = "Run Python Script"
+
+ use_selection_as_parent = False
+
+ def get_placeholder_options(self, options=None):
+ options = options or {}
+ return [
+ NumberDef(
+ "order",
+ label="Order",
+ default=options.get("order") or 0,
+ decimals=0,
+ minimum=0,
+ maximum=999,
+ tooltip=(
+ "Order"
+ "\nOrder defines asset loading priority (0 to 999)"
+ "\nPriority rule is : \"lowest is first to load\"."
+ )
+ ),
+ TextDef(
+ "prepare_script",
+ label="Run at\nprepare",
+ tooltip="Run before populate at prepare order",
+ multiline=True,
+ default=options.get("prepare_script", "")
+ ),
+ TextDef(
+ "populate_script",
+ label="Run at\npopulate",
+ tooltip="Run script at populate node order
"
+ "This is the default behavior",
+ multiline=True,
+ default=options.get("populate_script", EXAMPLE_SCRIPT)
+ ),
+ TextDef(
+ "depth_processed_script",
+ label="Run after\ndepth\niteration",
+ tooltip="Run script after every build depth iteration",
+ multiline=True,
+ default=options.get("depth_processed_script", "")
+ ),
+ TextDef(
+ "finished_script",
+ label="Run after\nbuild",
+ tooltip=(
+ "Run script at build finished.
"
+ "Note: this even runs if other placeholders had "
+ "errors during the build"
+ ),
+ multiline=True,
+ default=options.get("finished_script", "")
+ ),
+ EnumDef(
+ "create_nodetype",
+ label="Nodetype",
+ items={
+ "spaceLocator": "Locator",
+ "objectSet": "ObjectSet"
+ },
+ tooltip=(
+ "The placeholder's node type to be created.
"
+ "Note this only works on create, not on update"
+ ),
+ default=options.get("create_nodetype", "objectSet")
+ ),
+ ]
+
+ def create_placeholder(self, placeholder_data):
+ nodetype = placeholder_data.get("create_nodetype", "objectSet")
+
+ if nodetype == "spaceLocator":
+ super(MayaPlaceholderScriptPlugin, self).create_placeholder(
+ placeholder_data
+ )
+ elif nodetype == "objectSet":
+ placeholder_data["plugin_identifier"] = self.identifier
+
+ # Create maya objectSet on selection
+ selection = cmds.ls(selection=True, long=True)
+ name = self._create_placeholder_name(placeholder_data)
+ node = cmds.sets(selection, name=name)
+
+ self.imprint(node, placeholder_data)
+
+ def prepare_placeholders(self, placeholders):
+ super(MayaPlaceholderScriptPlugin, self).prepare_placeholders(
+ placeholders
+ )
+ for placeholder in placeholders:
+ prepare_script = placeholder.data.get("prepare_script")
+ if not prepare_script:
+ continue
+
+ self.run_script(placeholder, prepare_script)
+
+ def populate_placeholder(self, placeholder):
+
+ populate_script = placeholder.data.get("populate_script")
+ depth_script = placeholder.data.get("depth_processed_script")
+ finished_script = placeholder.data.get("finished_script")
+
+ # Run now
+ if populate_script:
+ self.run_script(placeholder, populate_script)
+
+ if not any([depth_script, finished_script]):
+ # No callback scripts to run
+ if not placeholder.data.get("keep_placeholder", True):
+ self.delete_placeholder(placeholder)
+ return
+
+ # Run at each depth processed
+ if depth_script:
+ callback = weakref_partial(
+ self.run_script, placeholder, depth_script)
+ self.builder.add_on_depth_processed_callback(
+ callback, order=placeholder.order)
+
+ # Run at build finish
+ if finished_script:
+ callback = weakref_partial(
+ self.run_script, placeholder, finished_script)
+ self.builder.add_on_finished_callback(
+ callback, order=placeholder.order)
+
+ # If placeholder should be deleted, delete it after finish so
+ # the scripts have access to it up to the last run
+ if not placeholder.data.get("keep_placeholder", True):
+ delete_callback = weakref_partial(
+ self.delete_placeholder, placeholder)
+ self.builder.add_on_finished_callback(
+ delete_callback, order=placeholder.order + 1)
+
+ def run_script(self, placeholder, script, event=None):
+ """Run script
+
+ Even though `placeholder` is an unused arguments by exposing it as
+ an input argument it means it makes it available through
+ globals()/locals() in the `exec` call, giving the script access
+ to the placeholder.
+
+ For example:
+ >>> node = placeholder.scene_identifier
+
+ In the case the script is running at a callback level (not during
+ populate) then it has access to the `event` as well, otherwise the
+ value is None if it runs during `populate_placeholder` directly.
+
+ For example adding this as the callback script:
+ >>> if event is not None:
+ >>> if event.topic == "on_depth_processed":
+ >>> print(f"Processed depth: {event.get('depth')}")
+ >>> elif event.topic == "on_finished":
+ >>> print("Build finished.")
+
+ """
+ self.log.debug(f"Running script at event: {event}")
+ exec(script, locals())
diff --git a/client/ayon_core/hosts/maya/startup/userSetup.py b/server_addon/maya/client/ayon_maya/startup/userSetup.py
similarity index 96%
rename from client/ayon_core/hosts/maya/startup/userSetup.py
rename to server_addon/maya/client/ayon_maya/startup/userSetup.py
index 3112e2bf12..600864fd2b 100644
--- a/client/ayon_core/hosts/maya/startup/userSetup.py
+++ b/server_addon/maya/client/ayon_maya/startup/userSetup.py
@@ -2,7 +2,7 @@ import os
from ayon_core.settings import get_project_settings
from ayon_core.pipeline import install_host, get_current_project_name
-from ayon_core.hosts.maya.api import MayaHost
+from ayon_maya.api import MayaHost
from maya import cmds
diff --git a/client/ayon_core/hosts/maya/tools/__init__.py b/server_addon/maya/client/ayon_maya/tools/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/__init__.py
rename to server_addon/maya/client/ayon_maya/tools/__init__.py
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/LICENSE b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/LICENSE
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/LICENSE
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/LICENSE
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/__init__.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/__init__.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/__init__.py
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/alembic.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/alembic.py
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/alembic.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/alembic.py
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/app.py
similarity index 99%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/app.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/app.py
index 44d8dfda21..2937b72934 100644
--- a/client/ayon_core/hosts/maya/tools/mayalookassigner/app.py
+++ b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/app.py
@@ -8,7 +8,7 @@ from qtpy import QtWidgets, QtCore
from ayon_core import style
from ayon_core.pipeline import get_current_project_name
from ayon_core.tools.utils.lib import qt_app_context
-from ayon_core.hosts.maya.api.lib import (
+from ayon_maya.api.lib import (
assign_look_by_version,
get_main_window
)
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/arnold_standin.py
similarity index 99%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/arnold_standin.py
index a20880dffc..c285b857c7 100644
--- a/client/ayon_core/hosts/maya/tools/mayalookassigner/arnold_standin.py
+++ b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/arnold_standin.py
@@ -7,7 +7,7 @@ from maya import cmds
import ayon_api
from ayon_core.pipeline import get_current_project_name
-from ayon_core.hosts.maya import api
+from ayon_maya import api
from . import lib
from .alembic import get_alembic_ids_cache
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/commands.py
similarity index 99%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/commands.py
index ad43a24385..54b1cff740 100644
--- a/client/ayon_core/hosts/maya/tools/mayalookassigner/commands.py
+++ b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/commands.py
@@ -10,7 +10,7 @@ from ayon_core.pipeline import (
registered_host,
get_current_project_name,
)
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
from .vray_proxies import get_alembic_ids_cache
from . import arnold_standin
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/lib.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/lib.py
similarity index 98%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/lib.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/lib.py
index 78fded12a9..5417db26bc 100644
--- a/client/ayon_core/hosts/maya/tools/mayalookassigner/lib.py
+++ b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/lib.py
@@ -11,7 +11,7 @@ from ayon_core.pipeline import (
loaders_from_representation,
load_container
)
-from ayon_core.hosts.maya.api import lib
+from ayon_maya.api import lib
log = logging.getLogger(__name__)
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/models.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/models.py
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/models.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/models.py
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/usd.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/usd.py
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/usd.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/usd.py
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/views.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/views.py
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/views.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/views.py
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/vray_proxies.py
similarity index 98%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/vray_proxies.py
index c1d9f019e4..6b451abb9a 100644
--- a/client/ayon_core/hosts/maya/tools/mayalookassigner/vray_proxies.py
+++ b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/vray_proxies.py
@@ -7,7 +7,7 @@ from maya import cmds
import ayon_api
from ayon_core.pipeline import get_current_project_name
-import ayon_core.hosts.maya.api.lib as maya_lib
+import ayon_maya.api.lib as maya_lib
from . import lib
from .alembic import get_alembic_ids_cache
diff --git a/client/ayon_core/hosts/maya/tools/mayalookassigner/widgets.py b/server_addon/maya/client/ayon_maya/tools/mayalookassigner/widgets.py
similarity index 100%
rename from client/ayon_core/hosts/maya/tools/mayalookassigner/widgets.py
rename to server_addon/maya/client/ayon_maya/tools/mayalookassigner/widgets.py
diff --git a/client/ayon_core/hosts/maya/vendor/python/capture.py b/server_addon/maya/client/ayon_maya/vendor/python/capture.py
similarity index 100%
rename from client/ayon_core/hosts/maya/vendor/python/capture.py
rename to server_addon/maya/client/ayon_maya/vendor/python/capture.py
diff --git a/server_addon/maya/package.py b/server_addon/maya/package.py
index 5c6ce923aa..274f74869b 100644
--- a/server_addon/maya/package.py
+++ b/server_addon/maya/package.py
@@ -1,3 +1,7 @@
name = "maya"
title = "Maya"
-version = "0.1.17"
+version = "0.2.0"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py
index bc38d5f746..9c552e17fa 100644
--- a/server_addon/maya/server/settings/publishers.py
+++ b/server_addon/maya/server/settings/publishers.py
@@ -46,7 +46,6 @@ def extract_alembic_overrides_enum():
return [
{"label": "Custom Attributes", "value": "attr"},
{"label": "Custom Attributes Prefix", "value": "attrPrefix"},
- {"label": "Auto Subd", "value": "autoSubd"},
{"label": "Data Format", "value": "dataFormat"},
{"label": "Euler Filter", "value": "eulerFilter"},
{"label": "Mel Per Frame Callback", "value": "melPerFrameCallback"},
@@ -347,17 +346,6 @@ class ExtractAlembicModel(BaseSettingsModel):
families: list[str] = SettingsField(
default_factory=list,
title="Families")
- autoSubd: bool = SettingsField(
- title="Auto Subd",
- description=(
- "If this flag is present and the mesh has crease edges, crease "
- "vertices or holes, the mesh (OPolyMesh) would now be written out "
- "as an OSubD and crease info will be stored in the Alembic file. "
- "Otherwise, creases info won't be preserved in Alembic file unless"
- " a custom Boolean attribute SubDivisionMesh has been added to "
- "mesh node and its value is true."
- )
- )
eulerFilter: bool = SettingsField(
title="Euler Filter",
description="Apply Euler filter while sampling rotations."
@@ -409,6 +397,10 @@ class ExtractAlembicModel(BaseSettingsModel):
title="Write Color Sets",
description="Write vertex colors with the geometry."
)
+ writeCreases: bool = SettingsField(
+ title="Write Creases",
+ description="Write the geometry's edge and vertex crease information."
+ )
writeFaceSets: bool = SettingsField(
title="Write Face Sets",
description="Write face sets with the geometry."
@@ -642,10 +634,6 @@ class PublishersModel(BaseSettingsModel):
title="Validate Instance In Context",
section="Validators"
)
- ValidateContainers: BasicValidateModel = SettingsField(
- default_factory=BasicValidateModel,
- title="Validate Containers"
- )
ValidateFrameRange: ValidateFrameRangeModel = SettingsField(
default_factory=ValidateFrameRangeModel,
title="Validate Frame Range"
@@ -925,10 +913,6 @@ class PublishersModel(BaseSettingsModel):
default_factory=BasicValidateModel,
title="Validate Rig Controllers",
)
- ValidateAnimatedReferenceRig: BasicValidateModel = SettingsField(
- default_factory=BasicValidateModel,
- title="Validate Animated Reference Rig",
- )
ValidateAnimationContent: BasicValidateModel = SettingsField(
default_factory=BasicValidateModel,
title="Validate Animation Content",
@@ -1071,11 +1055,6 @@ DEFAULT_PUBLISH_SETTINGS = {
"optional": True,
"active": True
},
- "ValidateContainers": {
- "enabled": True,
- "optional": True,
- "active": True
- },
"ValidateFrameRange": {
"enabled": True,
"optional": True,
@@ -1455,11 +1434,6 @@ DEFAULT_PUBLISH_SETTINGS = {
"optional": True,
"active": True
},
- "ValidateAnimatedReferenceRig": {
- "enabled": True,
- "optional": False,
- "active": True
- },
"ValidateAnimationContent": {
"enabled": True,
"optional": False,
@@ -1617,7 +1591,6 @@ DEFAULT_PUBLISH_SETTINGS = {
],
"attr": "",
"attrPrefix": "",
- "autoSubd": False,
"bake_attributes": [],
"bake_attribute_prefixes": [],
"dataFormat": "ogawa",
@@ -1641,7 +1614,7 @@ DEFAULT_PUBLISH_SETTINGS = {
"renderableOnly": False,
"stripNamespaces": True,
"uvsOnly": False,
- "uvWrite": False,
+ "uvWrite": True,
"userAttr": "",
"userAttrPrefix": "",
"verbose": False,
@@ -1649,6 +1622,7 @@ DEFAULT_PUBLISH_SETTINGS = {
"wholeFrameGeo": False,
"worldSpace": True,
"writeColorSets": False,
+ "writeCreases": False,
"writeFaceSets": False,
"writeNormals": True,
"writeUVSets": False,
diff --git a/client/ayon_core/hosts/nuke/__init__.py b/server_addon/nuke/client/ayon_nuke/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/__init__.py
rename to server_addon/nuke/client/ayon_nuke/__init__.py
diff --git a/client/ayon_core/hosts/nuke/addon.py b/server_addon/nuke/client/ayon_nuke/addon.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/addon.py
rename to server_addon/nuke/client/ayon_nuke/addon.py
diff --git a/client/ayon_core/hosts/nuke/api/__init__.py b/server_addon/nuke/client/ayon_nuke/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/api/__init__.py
rename to server_addon/nuke/client/ayon_nuke/api/__init__.py
diff --git a/client/ayon_core/hosts/nuke/api/actions.py b/server_addon/nuke/client/ayon_nuke/api/actions.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/api/actions.py
rename to server_addon/nuke/client/ayon_nuke/api/actions.py
diff --git a/client/ayon_core/hosts/nuke/api/command.py b/server_addon/nuke/client/ayon_nuke/api/command.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/api/command.py
rename to server_addon/nuke/client/ayon_nuke/api/command.py
diff --git a/client/ayon_core/hosts/nuke/api/constants.py b/server_addon/nuke/client/ayon_nuke/api/constants.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/api/constants.py
rename to server_addon/nuke/client/ayon_nuke/api/constants.py
diff --git a/client/ayon_core/hosts/nuke/api/gizmo_menu.py b/server_addon/nuke/client/ayon_nuke/api/gizmo_menu.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/api/gizmo_menu.py
rename to server_addon/nuke/client/ayon_nuke/api/gizmo_menu.py
diff --git a/client/ayon_core/hosts/nuke/api/lib.py b/server_addon/nuke/client/ayon_nuke/api/lib.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/api/lib.py
rename to server_addon/nuke/client/ayon_nuke/api/lib.py
index e3505a16f2..09dab4687a 100644
--- a/client/ayon_core/hosts/nuke/api/lib.py
+++ b/server_addon/nuke/client/ayon_nuke/api/lib.py
@@ -43,7 +43,9 @@ from ayon_core.pipeline import (
from ayon_core.pipeline.context_tools import (
get_current_context_custom_workfile_template
)
-from ayon_core.pipeline.colorspace import get_imageio_config
+from ayon_core.pipeline.colorspace import (
+ get_current_context_imageio_config_preset
+)
from ayon_core.pipeline.workfile import BuildWorkfile
from . import gizmo_menu
from .constants import ASSIST
@@ -352,7 +354,7 @@ def imprint(node, data, tab=None):
Examples:
```
import nuke
- from ayon_core.hosts.nuke.api import lib
+ from ayon_nuke.api import lib
node = nuke.createNode("NoOp")
data = {
@@ -417,7 +419,7 @@ def add_publish_knob(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:"):
"""[DEPRECATED] Sets data into nodes's avalon knob
@@ -483,7 +485,7 @@ def set_avalon_knob_data(node, data=None, prefix="avalon:"):
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):
"""[DEPRECATED] Gets a data from nodes's avalon knob
@@ -1022,6 +1024,18 @@ def script_name():
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):
name = "createReadNode"
label = "Read From Rendered"
@@ -1144,6 +1158,17 @@ def create_write_node(
Return:
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 []
# filtering variables
@@ -1268,6 +1293,10 @@ def create_write_node(
link.setFlag(0x1000)
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
add_button_write_to_read(GN)
@@ -1552,10 +1581,7 @@ class WorkfileSettings(object):
imageio_host (dict): host colorspace configurations
'''
- config_data = get_imageio_config(
- project_name=get_current_project_name(),
- host_name="nuke"
- )
+ config_data = get_current_context_imageio_config_preset()
workfile_settings = imageio_host["workfile"]
color_management = workfile_settings["color_management"]
@@ -2443,7 +2469,7 @@ def _launch_workfile_app():
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():
""" [DEPRECATED] Process workfile builder on nuke start
diff --git a/client/ayon_core/hosts/nuke/api/pipeline.py b/server_addon/nuke/client/ayon_nuke/api/pipeline.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/api/pipeline.py
rename to server_addon/nuke/client/ayon_nuke/api/pipeline.py
index d35a2e89e0..ad8e17b1f6 100644
--- a/client/ayon_core/hosts/nuke/api/pipeline.py
+++ b/server_addon/nuke/client/ayon_nuke/api/pipeline.py
@@ -28,7 +28,7 @@ from ayon_core.pipeline import (
)
from ayon_core.pipeline.workfile import BuildWorkfile
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 .lib import (
@@ -188,10 +188,10 @@ def reload_config():
"""
for module in (
- "ayon_core.hosts.nuke.api.actions",
- "ayon_core.hosts.nuke.api.menu",
- "ayon_core.hosts.nuke.api.plugin",
- "ayon_core.hosts.nuke.api.lib",
+ "ayon_nuke.api.actions",
+ "ayon_nuke.api.menu",
+ "ayon_nuke.api.plugin",
+ "ayon_nuke.api.lib",
):
log.info("Reloading module: {}...".format(module))
diff --git a/client/ayon_core/hosts/nuke/api/plugin.py b/server_addon/nuke/client/ayon_nuke/api/plugin.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/api/plugin.py
rename to server_addon/nuke/client/ayon_nuke/api/plugin.py
index fb56dec833..4f05cd41b9 100644
--- a/client/ayon_core/hosts/nuke/api/plugin.py
+++ b/server_addon/nuke/client/ayon_nuke/api/plugin.py
@@ -572,8 +572,11 @@ class ExporterReview(object):
self.fhead = self.fhead.replace("#", "")[:-1]
def get_representation_data(
- self, tags=None, range=False,
- custom_tags=None, colorspace=None
+ self,
+ tags=None,
+ range=False,
+ custom_tags=None,
+ colorspace=None,
):
""" Add representation data to self.data
@@ -584,6 +587,8 @@ class ExporterReview(object):
Defaults to False.
custom_tags (list[str], optional): user inputted custom tags.
Defaults to None.
+ colorspace (str, optional): colorspace name.
+ Defaults to None.
"""
add_tags = tags or []
repre = {
@@ -591,7 +596,13 @@ class ExporterReview(object):
"ext": self.ext,
"files": self.file,
"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:
@@ -778,6 +789,7 @@ class ExporterReviewMov(ExporterReview):
# deal with now lut defined in viewer lut
self.viewer_lut_raw = klass.viewer_lut_raw
self.write_colorspace = instance.data["colorspace"]
+ self.color_channels = instance.data["color_channels"]
self.name = name or "baked"
self.ext = ext or "mov"
@@ -834,9 +846,10 @@ class ExporterReviewMov(ExporterReview):
self.log.info("Nodes exported...")
return path
- def generate_mov(self, farm=False, **kwargs):
+ def generate_mov(self, farm=False, delete=True, **kwargs):
# colorspace data
- colorspace = None
+ colorspace = self.write_colorspace
+
# get colorspace settings
# get colorspace data from context
config_data, _ = get_colorspace_settings_from_publish_context(
@@ -947,6 +960,8 @@ class ExporterReviewMov(ExporterReview):
self.log.debug("Path: {}".format(self.path))
write_node["file"].setValue(str(self.path))
write_node["file_type"].setValue(str(self.ext))
+ write_node["channels"].setValue(str(self.color_channels))
+
# Knobs `meta_codec` and `mov64_codec` are not available on centos.
# TODO shouldn't this come from settings on outputs?
try:
@@ -987,11 +1002,16 @@ class ExporterReviewMov(ExporterReview):
self.render(write_node.name())
# ---------- generate representation data
+ tags = ["review", "need_thumbnail"]
+
+ if delete:
+ tags.append("delete")
+
self.get_representation_data(
- tags=["review", "need_thumbnail", "delete"] + add_tags,
+ tags=tags + add_tags,
custom_tags=add_custom_tags,
range=True,
- colorspace=colorspace
+ colorspace=colorspace,
)
self.log.debug("Representation... `{}`".format(self.data))
@@ -1030,7 +1050,7 @@ def convert_to_valid_instaces():
}
return mapping[product_type]
- from ayon_core.hosts.nuke.api import workio
+ from ayon_nuke.api import workio
task_name = get_current_task_name()
diff --git a/client/ayon_core/hosts/nuke/api/utils.py b/server_addon/nuke/client/ayon_nuke/api/utils.py
similarity index 63%
rename from client/ayon_core/hosts/nuke/api/utils.py
rename to server_addon/nuke/client/ayon_nuke/api/utils.py
index 1bfc1919fa..646bb0ece1 100644
--- a/client/ayon_core/hosts/nuke/api/utils.py
+++ b/server_addon/nuke/client/ayon_nuke/api/utils.py
@@ -3,9 +3,15 @@ import re
import nuke
-from ayon_core import resources
+import pyblish.util
+import pyblish.api
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):
""" Adding favorite folders to nuke's browser
@@ -142,3 +148,77 @@ def is_headless():
bool: headless
"""
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."
+ )
diff --git a/client/ayon_core/hosts/nuke/api/workfile_template_builder.py b/server_addon/nuke/client/ayon_nuke/api/workfile_template_builder.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/api/workfile_template_builder.py
rename to server_addon/nuke/client/ayon_nuke/api/workfile_template_builder.py
diff --git a/client/ayon_core/hosts/nuke/api/workio.py b/server_addon/nuke/client/ayon_nuke/api/workio.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/api/workio.py
rename to server_addon/nuke/client/ayon_nuke/api/workio.py
diff --git a/client/ayon_core/hosts/nuke/hooks/pre_nukeassist_setup.py b/server_addon/nuke/client/ayon_nuke/hooks/pre_nukeassist_setup.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/hooks/pre_nukeassist_setup.py
rename to server_addon/nuke/client/ayon_nuke/hooks/pre_nukeassist_setup.py
diff --git a/client/ayon_core/hosts/maya/plugins/publish/__init__.py b/server_addon/nuke/client/ayon_nuke/plugins/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/maya/plugins/publish/__init__.py
rename to server_addon/nuke/client/ayon_nuke/plugins/__init__.py
diff --git a/client/ayon_core/hosts/nuke/plugins/__init__.py b/server_addon/nuke/client/ayon_nuke/plugins/create/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/__init__.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/__init__.py
diff --git a/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py b/server_addon/nuke/client/ayon_nuke/plugins/create/convert_legacy.py
similarity index 93%
rename from client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/convert_legacy.py
index 8fb8abfbbf..65e719d15b 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/convert_legacy.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/convert_legacy.py
@@ -1,12 +1,12 @@
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
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,
get_node_data,
get_avalon_knob_data,
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
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_backdrop.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_backdrop.py
similarity index 95%
rename from client/ayon_core/hosts/nuke/plugins/create/create_backdrop.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_backdrop.py
index cefd9501ec..f97b9efeb6 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_backdrop.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_backdrop.py
@@ -1,6 +1,6 @@
from nukescripts import autoBackdrop
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
NukeCreator,
maintained_selection,
select_nodes
@@ -10,6 +10,8 @@ from ayon_core.hosts.nuke.api import (
class CreateBackdrop(NukeCreator):
"""Add Publishable Backdrop"""
+ settings_category = "nuke"
+
identifier = "create_backdrop"
label = "Nukenodes (backdrop)"
product_type = "nukenodes"
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_camera.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_camera.py
similarity index 95%
rename from client/ayon_core/hosts/nuke/plugins/create/create_camera.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_camera.py
index 764de84dcf..69e5b9c676 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_camera.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_camera.py
@@ -1,10 +1,10 @@
import nuke
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
NukeCreator,
NukeCreatorError,
maintained_selection
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
create_camera_node_by_version
)
@@ -12,6 +12,8 @@ from ayon_core.hosts.nuke.api.lib import (
class CreateCamera(NukeCreator):
"""Add Publishable Camera"""
+ settings_category = "nuke"
+
identifier = "create_camera"
label = "Camera (3d)"
product_type = "camera"
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_gizmo.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_gizmo.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/create/create_gizmo.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_gizmo.py
index ccc6aa13bd..6be7cd58db 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_gizmo.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_gizmo.py
@@ -1,5 +1,5 @@
import nuke
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
NukeCreator,
NukeCreatorError,
maintained_selection
@@ -9,6 +9,8 @@ from ayon_core.hosts.nuke.api import (
class CreateGizmo(NukeCreator):
"""Add Publishable Group as gizmo"""
+ settings_category = "nuke"
+
identifier = "create_gizmo"
label = "Gizmo (group)"
product_type = "gizmo"
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_model.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_model.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/create/create_model.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_model.py
index 507b7a1b57..b7d7b740c2 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_model.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_model.py
@@ -1,5 +1,5 @@
import nuke
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
NukeCreator,
NukeCreatorError,
maintained_selection
@@ -9,6 +9,8 @@ from ayon_core.hosts.nuke.api import (
class CreateModel(NukeCreator):
"""Add Publishable Camera"""
+ settings_category = "nuke"
+
identifier = "create_model"
label = "Model (3d)"
product_type = "model"
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_source.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_source.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/create/create_source.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_source.py
index ac6b8f694b..1579cebb1d 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_source.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_source.py
@@ -1,7 +1,7 @@
import nuke
import six
import sys
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
INSTANCE_DATA_KNOB,
NukeCreator,
NukeCreatorError,
@@ -15,6 +15,8 @@ from ayon_core.pipeline import (
class CreateSource(NukeCreator):
"""Add Publishable Read with source"""
+ settings_category = "nuke"
+
identifier = "create_source"
label = "Source (read)"
product_type = "source"
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_write_image.py
similarity index 91%
rename from client/ayon_core/hosts/nuke/plugins/create/create_write_image.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_write_image.py
index 770726e34f..2268817e76 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_write_image.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_write_image.py
@@ -11,11 +11,14 @@ from ayon_core.lib import (
UISeparatorDef,
EnumDef
)
-from ayon_core.hosts.nuke import api as napi
-from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs
+from ayon_nuke import api as napi
+from ayon_nuke.api.plugin import exposed_write_knobs
class CreateWriteImage(napi.NukeWriteCreator):
+
+ settings_category = "nuke"
+
identifier = "create_write_image"
label = "Image (write)"
product_type = "image"
@@ -65,12 +68,16 @@ class CreateWriteImage(napi.NukeWriteCreator):
)
def create_instance_node(self, product_name, instance_data):
+ settings = self.project_settings["nuke"]["create"]["CreateWriteImage"]
# add fpath_template
write_data = {
"creator": self.__class__.__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)
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_write_prerender.py
similarity index 90%
rename from client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_write_prerender.py
index 96ac2fac9c..014e91e81c 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_write_prerender.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_write_prerender.py
@@ -8,11 +8,14 @@ from ayon_core.pipeline import (
from ayon_core.lib import (
BoolDef
)
-from ayon_core.hosts.nuke import api as napi
-from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs
+from ayon_nuke import api as napi
+from ayon_nuke.api.plugin import exposed_write_knobs
class CreateWritePrerender(napi.NukeWriteCreator):
+
+ settings_category = "nuke"
+
identifier = "create_write_prerender"
label = "Prerender (write)"
product_type = "prerender"
@@ -46,11 +49,17 @@ class CreateWritePrerender(napi.NukeWriteCreator):
return attr_defs
def create_instance_node(self, product_name, instance_data):
+ settings = self.project_settings["nuke"]["create"]
+ settings = settings["CreateWritePrerender"]
+
# add fpath_template
write_data = {
"creator": self.__class__.__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)
diff --git a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py b/server_addon/nuke/client/ayon_nuke/plugins/create/create_write_render.py
similarity index 89%
rename from client/ayon_core/hosts/nuke/plugins/create/create_write_render.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/create_write_render.py
index 24bddb3d26..bed081c882 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/create_write_render.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/create_write_render.py
@@ -8,11 +8,14 @@ from ayon_core.pipeline import (
from ayon_core.lib import (
BoolDef
)
-from ayon_core.hosts.nuke import api as napi
-from ayon_core.hosts.nuke.api.plugin import exposed_write_knobs
+from ayon_nuke import api as napi
+from ayon_nuke.api.plugin import exposed_write_knobs
class CreateWriteRender(napi.NukeWriteCreator):
+
+ settings_category = "nuke"
+
identifier = "create_write_render"
label = "Render (write)"
product_type = "render"
@@ -40,11 +43,16 @@ class CreateWriteRender(napi.NukeWriteCreator):
return attr_defs
def create_instance_node(self, product_name, instance_data):
+ settings = self.project_settings["nuke"]["create"]["CreateWriteRender"]
+
# add fpath_template
write_data = {
"creator": self.__class__.__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)
diff --git a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py b/server_addon/nuke/client/ayon_nuke/plugins/create/workfile_creator.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py
rename to server_addon/nuke/client/ayon_nuke/plugins/create/workfile_creator.py
index b9d83a2b48..463d898224 100644
--- a/client/ayon_core/hosts/nuke/plugins/create/workfile_creator.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/create/workfile_creator.py
@@ -1,11 +1,11 @@
import ayon_api
-import ayon_core.hosts.nuke.api as api
+import ayon_nuke.api as api
from ayon_core.pipeline import (
AutoCreator,
CreatedInstance,
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
INSTANCE_DATA_KNOB,
set_node_data
)
@@ -13,6 +13,9 @@ import nuke
class WorkfileCreator(AutoCreator):
+
+ settings_category = "nuke"
+
identifier = "workfile"
product_type = "workfile"
diff --git a/client/ayon_core/hosts/nuke/plugins/inventory/repair_old_loaders.py b/server_addon/nuke/client/ayon_nuke/plugins/inventory/repair_old_loaders.py
similarity index 94%
rename from client/ayon_core/hosts/nuke/plugins/inventory/repair_old_loaders.py
rename to server_addon/nuke/client/ayon_nuke/plugins/inventory/repair_old_loaders.py
index 7bb5c8ef20..11d65d4b8c 100644
--- a/client/ayon_core/hosts/nuke/plugins/inventory/repair_old_loaders.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/inventory/repair_old_loaders.py
@@ -1,6 +1,6 @@
from ayon_core.lib import Logger
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):
diff --git a/client/ayon_core/hosts/nuke/plugins/inventory/select_containers.py b/server_addon/nuke/client/ayon_nuke/plugins/inventory/select_containers.py
similarity index 88%
rename from client/ayon_core/hosts/nuke/plugins/inventory/select_containers.py
rename to server_addon/nuke/client/ayon_nuke/plugins/inventory/select_containers.py
index 2fa9c06984..f67c8c16e9 100644
--- a/client/ayon_core/hosts/nuke/plugins/inventory/select_containers.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/inventory/select_containers.py
@@ -1,5 +1,5 @@
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):
diff --git a/client/ayon_core/hosts/nuke/plugins/load/actions.py b/server_addon/nuke/client/ayon_nuke/plugins/load/actions.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/load/actions.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/actions.py
index 53cb03087b..a4e2b156a3 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/actions.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/actions.py
@@ -4,7 +4,7 @@
from ayon_core.lib import Logger
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__)
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_backdrop.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_backdrop.py
index 7d823919dc..054a56d041 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_backdrop.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_backdrop.py
@@ -6,7 +6,7 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
find_free_space_to_paste_nodes,
maintained_selection,
reset_selection,
@@ -14,8 +14,8 @@ from ayon_core.hosts.nuke.api.lib import (
get_avalon_knob_data,
set_avalon_knob_data
)
-from ayon_core.hosts.nuke.api.command import viewer_update_and_undo_stop
-from ayon_core.hosts.nuke.api import containerise, update_container
+from ayon_nuke.api.command import viewer_update_and_undo_stop
+from ayon_nuke.api import containerise, update_container
class LoadBackdropNodes(load.LoaderPlugin):
@@ -25,6 +25,8 @@ class LoadBackdropNodes(load.LoaderPlugin):
representations = {"*"}
extensions = {"nk"}
+ settings_category = "nuke"
+
label = "Import Nuke Nodes"
order = 0
icon = "eye"
@@ -62,7 +64,7 @@ class LoadBackdropNodes(load.LoaderPlugin):
}
# add attributes from the version to imprint to metadata knob
- for k in ["source", "author", "fps"]:
+ for k in ["source", "fps"]:
data_imprint[k] = version_attributes[k]
# getting file path
@@ -206,7 +208,7 @@ class LoadBackdropNodes(load.LoaderPlugin):
"colorspaceInput": colorspace,
}
- for k in ["source", "author", "fps"]:
+ for k in ["source", "fps"]:
data_imprint[k] = version_attributes[k]
# adding nodes to node graph
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_camera_abc.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_camera_abc.py
index 14c54c3adc..3930cf52fa 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_camera_abc.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_camera_abc.py
@@ -5,12 +5,12 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
maintained_selection
)
@@ -24,6 +24,8 @@ class AlembicCameraLoader(load.LoaderPlugin):
representations = {"*"}
extensions = {"abc"}
+ settings_category = "nuke"
+
label = "Load Alembic Camera"
icon = "camera"
color = "orange"
@@ -48,7 +50,7 @@ class AlembicCameraLoader(load.LoaderPlugin):
"frameEnd": last,
"version": version_entity["version"],
}
- for k in ["source", "author", "fps"]:
+ for k in ["source", "fps"]:
data_imprint[k] = version_attributes[k]
# getting file path
@@ -123,7 +125,7 @@ class AlembicCameraLoader(load.LoaderPlugin):
}
# add attributes from the version to imprint to metadata knob
- for k in ["source", "author", "fps"]:
+ for k in ["source", "fps"]:
data_imprint[k] = version_attributes[k]
# getting file path
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_clip.py
similarity index 90%
rename from client/ayon_core/hosts/nuke/plugins/load/load_clip.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_clip.py
index df8f2ab018..d1e38eea6b 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_clip.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_clip.py
@@ -9,13 +9,14 @@ from ayon_core.pipeline import (
get_representation_path,
)
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,
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
get_imageio_input_colorspace,
maintained_selection
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop,
@@ -25,7 +26,7 @@ from ayon_core.lib.transcoding import (
VIDEO_EXTENSIONS,
IMAGE_EXTENSIONS
)
-from ayon_core.hosts.nuke.api import plugin
+from ayon_nuke.api import plugin
class LoadClip(plugin.NukeLoader):
@@ -47,6 +48,8 @@ class LoadClip(plugin.NukeLoader):
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
)
+ settings_category = "nuke"
+
label = "Load Clip"
order = -20
icon = "file-video-o"
@@ -60,7 +63,8 @@ class LoadClip(plugin.NukeLoader):
# option gui
options_defaults = {
"start_at_workfile": True,
- "add_retime": True
+ "add_retime": True,
+ "deep_exr": False
}
node_name_template = "{class_name}_{ext}"
@@ -77,6 +81,11 @@ class LoadClip(plugin.NukeLoader):
"add_retime",
help="Load with retime",
default=cls.options_defaults["add_retime"]
+ ),
+ qargparse.Boolean(
+ "deep_exr",
+ help="Read with deep exr",
+ default=cls.options_defaults["deep_exr"]
)
]
@@ -112,6 +121,9 @@ class LoadClip(plugin.NukeLoader):
add_retime = options.get(
"add_retime", self.options_defaults["add_retime"])
+ deep_exr = options.get(
+ "deep_exr", self.options_defaults["deep_exr"])
+
repre_id = repre_entity["id"]
self.log.debug(
@@ -152,13 +164,21 @@ class LoadClip(plugin.NukeLoader):
return
read_name = self._get_node_name(context)
-
- # Create the Loader with the filename path set
- read_node = nuke.createNode(
- "Read",
- "name {}".format(read_name),
- inpanel=False
- )
+ read_node = None
+ if deep_exr:
+ # Create the Loader with the filename path set
+ read_node = nuke.createNode(
+ "DeepRead",
+ "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
colorspace = (
@@ -170,14 +190,14 @@ class LoadClip(plugin.NukeLoader):
# we will switch off undo-ing
with viewer_update_and_undo_stop():
read_node["file"].setValue(filepath)
-
- self.set_colorspace_to_node(
- read_node,
- filepath,
- project_name,
- version_entity,
- repre_entity
- )
+ if read_node.Class() == "Read":
+ self.set_colorspace_to_node(
+ read_node,
+ filepath,
+ project_name,
+ version_entity,
+ repre_entity
+ )
self._set_range_to_node(
read_node, first, last, start_at_workfile, slate_frames
@@ -197,7 +217,6 @@ class LoadClip(plugin.NukeLoader):
"frameStart",
"frameEnd",
"source",
- "author",
"fps",
"handleStart",
"handleEnd",
@@ -328,13 +347,14 @@ class LoadClip(plugin.NukeLoader):
# to avoid multiple undo steps for rest of process
# we will switch off undo-ing
with viewer_update_and_undo_stop():
- self.set_colorspace_to_node(
- read_node,
- filepath,
- project_name,
- version_entity,
- repre_entity
- )
+ if read_node.Class() == "Read":
+ self.set_colorspace_to_node(
+ read_node,
+ filepath,
+ project_name,
+ version_entity,
+ repre_entity
+ )
self._set_range_to_node(read_node, first, last, start_at_workfile)
@@ -347,8 +367,7 @@ class LoadClip(plugin.NukeLoader):
"source": version_attributes.get("source"),
"handleStart": str(self.handle_start),
"handleEnd": str(self.handle_end),
- "fps": str(version_attributes.get("fps")),
- "author": version_attributes.get("author")
+ "fps": str(version_attributes.get("fps"))
}
last_version_entity = ayon_api.get_last_version_by_product_id(
@@ -547,9 +566,10 @@ class LoadClip(plugin.NukeLoader):
f"Colorspace from representation colorspaceData: {colorspace}"
)
+ config_data = get_current_context_imageio_config_preset()
# check if any filerules are not applicable
new_parsed_colorspace = get_imageio_file_rules_colorspace_from_filepath( # noqa
- filepath, "nuke", project_name
+ filepath, "nuke", project_name, config_data=config_data
)
self.log.debug(f"Colorspace new filerules: {new_parsed_colorspace}")
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_effects.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/load/load_effects.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_effects.py
index a87c81295a..e923a02424 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_effects.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_effects.py
@@ -8,7 +8,7 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
@@ -22,13 +22,14 @@ class LoadEffects(load.LoaderPlugin):
representations = {"*"}
extensions = {"json"}
+ settings_category = "nuke"
+
label = "Load Effects - nodes"
order = 0
icon = "cc"
color = "white"
ignore_attr = ["useLifetime"]
-
def load(self, context, name, namespace, data):
"""
Loading function to get the soft effects to particular read node
@@ -69,7 +70,6 @@ class LoadEffects(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
@@ -189,7 +189,6 @@ class LoadEffects(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps",
]:
data_imprint[k] = version_attributes[k]
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_effects_ip.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_effects_ip.py
index 8fa1347598..ce7e7debeb 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_effects_ip.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_effects_ip.py
@@ -8,8 +8,8 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api import lib
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import lib
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
@@ -23,6 +23,8 @@ class LoadEffectsInputProcess(load.LoaderPlugin):
representations = {"*"}
extensions = {"json"}
+ settings_category = "nuke"
+
label = "Load Effects - Input Process"
order = 0
icon = "eye"
@@ -69,7 +71,6 @@ class LoadEffectsInputProcess(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
@@ -192,7 +193,6 @@ class LoadEffectsInputProcess(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_gizmo.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_gizmo.py
index 95f85bacfc..1c91af0c1c 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_gizmo.py
@@ -5,13 +5,13 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
maintained_selection,
get_avalon_knob_data,
set_avalon_knob_data,
swap_node_with_dependency,
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
@@ -25,6 +25,8 @@ class LoadGizmo(load.LoaderPlugin):
representations = {"*"}
extensions = {"nk"}
+ settings_category = "nuke"
+
label = "Load Gizmo"
order = 0
icon = "dropbox"
@@ -71,7 +73,6 @@ class LoadGizmo(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
@@ -139,7 +140,6 @@ class LoadGizmo(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_gizmo_ip.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_gizmo_ip.py
index 3112e27811..36e878fdf1 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_gizmo_ip.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_gizmo_ip.py
@@ -6,14 +6,14 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
maintained_selection,
create_backdrop,
get_avalon_knob_data,
set_avalon_knob_data,
swap_node_with_dependency,
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
@@ -27,6 +27,8 @@ class LoadGizmoInputProcess(load.LoaderPlugin):
representations = {"*"}
extensions = {"nk"}
+ settings_category = "nuke"
+
label = "Load Gizmo - Input Process"
order = 0
icon = "eye"
@@ -73,7 +75,6 @@ class LoadGizmoInputProcess(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
@@ -145,7 +146,6 @@ class LoadGizmoInputProcess(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_image.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_image.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/load/load_image.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_image.py
index d825b621fc..0c43f5a5ca 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_image.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_image.py
@@ -7,10 +7,10 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
get_imageio_input_colorspace
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
@@ -33,9 +33,9 @@ class LoadImage(load.LoaderPlugin):
"image",
}
representations = {"*"}
- extensions = set(
- ext.lstrip(".") for ext in IMAGE_EXTENSIONS
- )
+ extensions = set(ext.lstrip(".") for ext in IMAGE_EXTENSIONS)
+
+ settings_category = "nuke"
label = "Load Image"
order = -10
@@ -133,7 +133,7 @@ class LoadImage(load.LoaderPlugin):
"version": version_entity["version"],
"colorspace": colorspace,
}
- for k in ["source", "author", "fps"]:
+ for k in ["source", "fps"]:
data_imprint[k] = version_attributes.get(k, str(None))
r["tile_color"].setValue(int("0x4ecd25ff", 16))
@@ -207,7 +207,6 @@ class LoadImage(load.LoaderPlugin):
"colorspace": version_attributes.get("colorSpace"),
"source": version_attributes.get("source"),
"fps": str(version_attributes.get("fps")),
- "author": version_attributes.get("author")
}
# change color of node
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_matchmove.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_matchmove.py
similarity index 95%
rename from client/ayon_core/hosts/nuke/plugins/load/load_matchmove.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_matchmove.py
index beebd0458f..c1b5a24504 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_matchmove.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_matchmove.py
@@ -11,6 +11,8 @@ class MatchmoveLoader(load.LoaderPlugin):
representations = {"*"}
extensions = {"py"}
+ settings_category = "nuke"
+
defaults = ["Camera", "Object"]
label = "Run matchmove script"
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_model.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_model.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/load/load_model.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_model.py
index 0326e0a4fc..551147be96 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_model.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_model.py
@@ -5,8 +5,8 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api.lib import maintained_selection
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api.lib import maintained_selection
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
@@ -22,6 +22,8 @@ class AlembicModelLoader(load.LoaderPlugin):
representations = {"*"}
extensions = {"abc"}
+ settings_category = "nuke"
+
label = "Load Alembic"
icon = "cube"
color = "orange"
@@ -47,7 +49,7 @@ class AlembicModelLoader(load.LoaderPlugin):
"version": version_entity["version"]
}
# add attributes from the version to imprint to metadata knob
- for k in ["source", "author", "fps"]:
+ for k in ["source", "fps"]:
data_imprint[k] = version_attributes[k]
# getting file path
@@ -130,7 +132,7 @@ class AlembicModelLoader(load.LoaderPlugin):
}
# add additional metadata from the version to imprint to Avalon knob
- for k in ["source", "author", "fps"]:
+ for k in ["source", "fps"]:
data_imprint[k] = version_attributes[k]
# getting file path
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_ociolook.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_ociolook.py
index c369030b65..bdff8d7e28 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_ociolook.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_ociolook.py
@@ -10,7 +10,7 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
containerise,
viewer_update_and_undo_stop,
update_container,
@@ -24,6 +24,8 @@ class LoadOcioLookNodes(load.LoaderPlugin):
representations = {"*"}
extensions = {"json"}
+ settings_category = "nuke"
+
label = "Load OcioLook [nodes]"
order = 0
icon = "cc"
diff --git a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py b/server_addon/nuke/client/ayon_nuke/plugins/load/load_script_precomp.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py
rename to server_addon/nuke/client/ayon_nuke/plugins/load/load_script_precomp.py
index 3e554f9d3b..cf543dabfd 100644
--- a/client/ayon_core/hosts/nuke/plugins/load/load_script_precomp.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/load/load_script_precomp.py
@@ -5,8 +5,8 @@ from ayon_core.pipeline import (
load,
get_representation_path,
)
-from ayon_core.hosts.nuke.api.lib import get_avalon_knob_data
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api.lib import get_avalon_knob_data
+from ayon_nuke.api import (
containerise,
update_container,
viewer_update_and_undo_stop
@@ -20,6 +20,8 @@ class LinkAsGroup(load.LoaderPlugin):
representations = {"*"}
extensions = {"nk"}
+ settings_category = "nuke"
+
label = "Load Precomp"
order = 0
icon = "file"
@@ -55,7 +57,6 @@ class LinkAsGroup(load.LoaderPlugin):
"handleStart",
"handleEnd",
"source",
- "author",
"fps"
]:
data_imprint[k] = version_attributes[k]
@@ -131,7 +132,6 @@ class LinkAsGroup(load.LoaderPlugin):
"colorspace": version_attributes.get("colorSpace"),
"source": version_attributes.get("source"),
"fps": version_attributes.get("fps"),
- "author": version_attributes.get("author")
}
# Update the imprinted representation
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_backdrop.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_backdrop.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_backdrop.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_backdrop.py
index fc17de95b4..1471159380 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_backdrop.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_backdrop.py
@@ -1,6 +1,6 @@
from pprint import pformat
import pyblish.api
-from ayon_core.hosts.nuke.api import lib as pnlib
+from ayon_nuke.api import lib as pnlib
import nuke
@@ -13,6 +13,8 @@ class CollectBackdrops(pyblish.api.InstancePlugin):
hosts = ["nuke"]
families = ["nukenodes"]
+ settings_category = "nuke"
+
def process(self, instance):
self.log.debug(pformat(instance.data))
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_context_data.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_context_data.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_context_data.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_context_data.py
index 0a032e5a2d..33c8e63e82 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_context_data.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_context_data.py
@@ -2,7 +2,7 @@ import os
import nuke
import pyblish.api
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
@@ -13,6 +13,8 @@ class CollectContextData(pyblish.api.ContextPlugin):
label = "Collect context data"
hosts = ['nuke']
+ settings_category = "nuke"
+
def process(self, context): # sourcery skip: avoid-builtin-shadow
root_node = nuke.root()
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_framerate.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_framerate.py
similarity index 91%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_framerate.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_framerate.py
index 88a449e745..cd77eab0f1 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_framerate.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_framerate.py
@@ -13,5 +13,7 @@ class CollectFramerate(pyblish.api.ContextPlugin):
"nukeassist"
]
+ settings_category = "nuke"
+
def process(self, context):
context.data["fps"] = nuke.root()["fps"].getValue()
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_gizmo.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_gizmo.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_gizmo.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_gizmo.py
index fda1c7ac31..ece9823b37 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_gizmo.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_gizmo.py
@@ -11,6 +11,8 @@ class CollectGizmo(pyblish.api.InstancePlugin):
hosts = ["nuke"]
families = ["gizmo"]
+ settings_category = "nuke"
+
def process(self, instance):
gizmo_node = instance.data["transientData"]["node"]
diff --git a/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_headless_farm.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_headless_farm.py
new file mode 100644
index 0000000000..c00b9a8f5d
--- /dev/null
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_headless_farm.py
@@ -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
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_model.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_model.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_model.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_model.py
index 1a2bc9c019..f4266bbbcb 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_model.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_model.py
@@ -11,6 +11,8 @@ class CollectModel(pyblish.api.InstancePlugin):
hosts = ["nuke"]
families = ["model"]
+ settings_category = "nuke"
+
def process(self, instance):
geo_node = instance.data["transientData"]["node"]
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_nuke_instance_data.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_nuke_instance_data.py
index 951072ff3f..d1392a8460 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_nuke_instance_data.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_nuke_instance_data.py
@@ -11,6 +11,8 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
label = "Collect Nuke Instance Data"
hosts = ["nuke", "nukeassist"]
+ settings_category = "nuke"
+
# presets
sync_workfile_version_on_families = []
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_reads.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_reads.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_reads.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_reads.py
index af17933eb1..439374e825 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_reads.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_reads.py
@@ -12,6 +12,8 @@ class CollectNukeReads(pyblish.api.InstancePlugin):
hosts = ["nuke", "nukeassist"]
families = ["source"]
+ settings_category = "nuke"
+
def process(self, instance):
self.log.debug("checking instance: {}".format(instance))
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_slate_node.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_slate_node.py
index ac30bd6051..bb3b0083ab 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_slate_node.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_slate_node.py
@@ -10,6 +10,8 @@ class CollectSlate(pyblish.api.InstancePlugin):
hosts = ["nuke"]
families = ["render"]
+ settings_category = "nuke"
+
def process(self, instance):
node = instance.data["transientData"]["node"]
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_workfile.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_workfile.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_workfile.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_workfile.py
index 0f03572f8b..e4bd5ed129 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_workfile.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_workfile.py
@@ -11,6 +11,8 @@ class CollectWorkfile(pyblish.api.InstancePlugin):
hosts = ['nuke']
families = ["workfile"]
+ settings_category = "nuke"
+
def process(self, instance): # sourcery skip: avoid-builtin-shadow
script_data = instance.context.data["scriptData"]
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_writes.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/collect_writes.py
index 745351dc49..816f493d72 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/collect_writes.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/collect_writes.py
@@ -1,7 +1,7 @@
import os
import nuke
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
@@ -14,6 +14,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin,
hosts = ["nuke", "nukeassist"]
families = ["render", "prerender", "image"]
+ settings_category = "nuke"
+
# cache
_write_nodes = {}
_frame_ranges = {}
@@ -153,6 +155,9 @@ class CollectNukeWrites(pyblish.api.InstancePlugin,
# Determine defined file type
ext = write_node["file_type"].value()
+ # determine defined channel type
+ color_channels = write_node["channels"].value()
+
# get frame range data
handle_start = instance.context.data["handleStart"]
handle_end = instance.context.data["handleEnd"]
@@ -172,7 +177,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin,
"path": write_file_path,
"outputDir": output_dir,
"ext": ext,
- "colorspace": colorspace
+ "colorspace": colorspace,
+ "color_channels": color_channels
})
if product_type == "render":
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_backdrop.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_backdrop.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_backdrop.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_backdrop.py
index e53ce9015a..8c42920979 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_backdrop.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_backdrop.py
@@ -5,7 +5,7 @@ import nuke
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
maintained_selection,
reset_selection,
select_nodes
@@ -25,6 +25,8 @@ class ExtractBackdropNode(publish.Extractor):
hosts = ["nuke"]
families = ["nukenodes"]
+ settings_category = "nuke"
+
def process(self, instance):
tmp_nodes = []
child_nodes = instance.data["transientData"]["childNodes"]
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_camera.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_camera.py
index a1a5acb63b..83914087e3 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_camera.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_camera.py
@@ -6,7 +6,7 @@ import nuke
import pyblish.api
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):
@@ -17,6 +17,8 @@ class ExtractCamera(publish.Extractor):
families = ["camera"]
hosts = ["nuke"]
+ settings_category = "nuke"
+
# presets
write_geo_knobs = [
("file_type", "abc"),
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_gizmo.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_gizmo.py
similarity index 95%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_gizmo.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_gizmo.py
index 2a2e2255fd..05e3164163 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_gizmo.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_gizmo.py
@@ -4,8 +4,8 @@ import nuke
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.nuke.api import utils as pnutils
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api import utils as pnutils
+from ayon_nuke.api.lib import (
maintained_selection,
reset_selection,
select_nodes
@@ -23,6 +23,8 @@ class ExtractGizmo(publish.Extractor):
hosts = ["nuke"]
families = ["gizmo"]
+ settings_category = "nuke"
+
def process(self, instance):
tmp_nodes = []
orig_grpn = instance.data["transientData"]["node"]
diff --git a/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_headless_farm.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_headless_farm.py
new file mode 100644
index 0000000000..4721fe4462
--- /dev/null
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_headless_farm.py
@@ -0,0 +1,38 @@
+import os
+from datetime import datetime
+import shutil
+
+import pyblish.api
+
+from ayon_core.pipeline import registered_host
+
+
+class ExtractRenderOnFarm(pyblish.api.InstancePlugin):
+ """Copy the workfile to a timestamped copy."""
+
+ order = pyblish.api.ExtractorOrder + 0.499
+ label = "Extract Render On Farm"
+ hosts = ["nuke"]
+ families = ["render_on_farm"]
+
+ settings_category = "nuke"
+
+ def process(self, instance):
+ if not instance.context.data.get("render_on_farm", False):
+ return
+
+ host = registered_host()
+ current_datetime = datetime.now()
+ formatted_timestamp = current_datetime.strftime("%Y%m%d%H%M%S")
+ base, ext = os.path.splitext(host.current_file())
+
+ directory = os.path.join(os.path.dirname(base), "farm_submissions")
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+ filename = "{}_{}{}".format(
+ os.path.basename(base), formatted_timestamp, ext
+ )
+ path = os.path.join(directory, filename).replace("\\", "/")
+ instance.context.data["currentFile"] = path
+ shutil.copy(host.current_file(), path)
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_model.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_model.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_model.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_model.py
index 36896fe595..58b9d4179b 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_model.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_model.py
@@ -4,7 +4,7 @@ import nuke
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
maintained_selection,
select_nodes
)
@@ -18,6 +18,8 @@ class ExtractModel(publish.Extractor):
families = ["model"]
hosts = ["nuke"]
+ settings_category = "nuke"
+
# presets
write_geo_knobs = [
("file_type", "abc"),
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_ouput_node.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_ouput_node.py
similarity index 92%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_ouput_node.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_ouput_node.py
index b8e038a4f5..52072cddc5 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_ouput_node.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_ouput_node.py
@@ -1,6 +1,6 @@
import nuke
import pyblish.api
-from ayon_core.hosts.nuke.api.lib import maintained_selection
+from ayon_nuke.api.lib import maintained_selection
class CreateOutputNode(pyblish.api.ContextPlugin):
@@ -11,7 +11,9 @@ class CreateOutputNode(pyblish.api.ContextPlugin):
label = 'Output Node Create'
order = pyblish.api.ExtractorOrder + 0.4
families = ["workfile"]
- hosts = ['nuke']
+ hosts = ["nuke"]
+
+ settings_category = "nuke"
def process(self, context):
# capture selection state
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_output_directory.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_output_directory.py
similarity index 94%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_output_directory.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_output_directory.py
index d999d200de..45156ca9ae 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_output_directory.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_output_directory.py
@@ -10,7 +10,7 @@ class ExtractOutputDirectory(pyblish.api.InstancePlugin):
label = "Output Directory"
optional = True
- # targets = ["process"]
+ settings_category = "nuke"
def process(self, instance):
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_render_local.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_render_local.py
index c8be2a5564..c865684e7a 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_render_local.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_render_local.py
@@ -4,7 +4,7 @@ import shutil
import pyblish.api
import clique
import nuke
-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.lib import collect_frames
@@ -25,6 +25,8 @@ class NukeRenderLocal(publish.Extractor,
hosts = ["nuke"]
families = ["render.local", "prerender.local", "image.local"]
+ settings_category = "nuke"
+
def process(self, instance):
child_nodes = (
instance.data.get("transientData", {}).get("childNodes")
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_data.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_data.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_review_data.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_data.py
index 258a019319..856616898b 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_data.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_data.py
@@ -16,6 +16,8 @@ class ExtractReviewData(publish.Extractor):
families = ["review"]
hosts = ["nuke"]
+ settings_category = "nuke"
+
def process(self, instance):
fpath = instance.data["path"]
ext = os.path.splitext(fpath)[-1][1:]
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_data_lut.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_data_lut.py
similarity index 94%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_review_data_lut.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_data_lut.py
index 0674a2dd55..d3377807ea 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_data_lut.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_data_lut.py
@@ -2,8 +2,8 @@ import os
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.nuke.api import plugin
-from ayon_core.hosts.nuke.api.lib import maintained_selection
+from ayon_nuke.api import plugin
+from ayon_nuke.api.lib import maintained_selection
class ExtractReviewDataLut(publish.Extractor):
@@ -19,6 +19,8 @@ class ExtractReviewDataLut(publish.Extractor):
families = ["review"]
hosts = ["nuke"]
+ settings_category = "nuke"
+
def process(self, instance):
self.log.debug("Creating staging dir...")
if "representations" in instance.data:
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_intermediates.py
similarity index 94%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_intermediates.py
index 8d7a3ec311..b7bb911347 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_review_intermediates.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_review_intermediates.py
@@ -4,8 +4,8 @@ from pprint import pformat
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.nuke.api import plugin
-from ayon_core.hosts.nuke.api.lib import maintained_selection
+from ayon_nuke.api import plugin
+from ayon_nuke.api.lib import maintained_selection
class ExtractReviewIntermediates(publish.Extractor):
@@ -22,6 +22,8 @@ class ExtractReviewIntermediates(publish.Extractor):
families = ["review"]
hosts = ["nuke"]
+ settings_category = "nuke"
+
# presets
viewer_lut_raw = None
outputs = {}
@@ -136,11 +138,15 @@ class ExtractReviewIntermediates(publish.Extractor):
self, instance, o_name, o_data["extension"],
multiple_presets)
+ delete = not o_data.get("publish", False)
+
if instance.data.get("farm"):
if "review" in instance.data["families"]:
instance.data["families"].remove("review")
- data = exporter.generate_mov(farm=True, **o_data)
+ data = exporter.generate_mov(
+ farm=True, delete=delete, **o_data
+ )
self.log.debug(
"_ data: {}".format(data))
@@ -154,7 +160,7 @@ class ExtractReviewIntermediates(publish.Extractor):
"bakeWriteNodeName": data.get("bakeWriteNodeName")
})
else:
- data = exporter.generate_mov(**o_data)
+ data = exporter.generate_mov(delete=delete, **o_data)
# add representation generated by exporter
generated_repres.extend(data["representations"])
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_script_save.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_script_save.py
similarity index 85%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_script_save.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_script_save.py
index d325684a7c..ea584b6529 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_script_save.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_script_save.py
@@ -6,7 +6,9 @@ class ExtractScriptSave(pyblish.api.InstancePlugin):
"""Save current Nuke workfile script"""
label = 'Script Save'
order = pyblish.api.ExtractorOrder - 0.1
- hosts = ['nuke']
+ hosts = ["nuke"]
+
+ settings_category = "nuke"
def process(self, instance):
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_slate_frame.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/extract_slate_frame.py
index 627888ac92..47750ea637 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/extract_slate_frame.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/extract_slate_frame.py
@@ -7,7 +7,7 @@ import pyblish.api
import six
from ayon_core.pipeline import publish
-from ayon_core.hosts.nuke.api import (
+from ayon_nuke.api import (
maintained_selection,
duplicate_node,
get_view_process_node
@@ -27,6 +27,8 @@ class ExtractSlateFrame(publish.Extractor):
families = ["slate"]
hosts = ["nuke"]
+ settings_category = "nuke"
+
# Settings values
key_value_mapping = {
"f_submission_note": {
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_asset_context.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_asset_context.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_asset_context.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_asset_context.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_backdrop.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_backdrop.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_backdrop.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_backdrop.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_gizmo.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_gizmo.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_gizmo.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_gizmo.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_knobs.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_knobs.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_knobs.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_knobs.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_output_resolution.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_output_resolution.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_output_resolution.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_output_resolution.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_proxy_mode.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_proxy_mode.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_proxy_mode.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_proxy_mode.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_rendered_frames.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_rendered_frames.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_rendered_frames.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_rendered_frames.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_script_attributes.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_script_attributes.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_script_attributes.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_script_attributes.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/help/validate_write_nodes.xml b/server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_write_nodes.xml
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/publish/help/validate_write_nodes.xml
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/help/validate_write_nodes.xml
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/increment_script_version.py
similarity index 82%
rename from client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/increment_script_version.py
index 6b0be42ba1..36659aa2d2 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/increment_script_version.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/increment_script_version.py
@@ -1,4 +1,3 @@
-
import nuke
import pyblish.api
@@ -10,9 +9,13 @@ class IncrementScriptVersion(pyblish.api.ContextPlugin):
label = "Increment Script Version"
optional = True
families = ["workfile"]
- hosts = ['nuke']
+ hosts = ["nuke"]
+
+ settings_category = "nuke"
def process(self, context):
+ if not context.data.get("increment_script_version", True):
+ return
assert all(result["success"] for result in context.data["results"]), (
"Publishing not successful so version is not increased.")
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/remove_ouput_node.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/remove_ouput_node.py
similarity index 91%
rename from client/ayon_core/hosts/nuke/plugins/publish/remove_ouput_node.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/remove_ouput_node.py
index fb77e8638c..4c17cb5f56 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/remove_ouput_node.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/remove_ouput_node.py
@@ -9,7 +9,9 @@ class RemoveOutputNode(pyblish.api.ContextPlugin):
label = 'Output Node Remove'
order = pyblish.api.IntegratorOrder + 0.4
families = ["workfile"]
- hosts = ['nuke']
+ hosts = ["nuke"]
+
+ settings_category = "nuke"
def process(self, context):
try:
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_asset_context.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_asset_context.py
index 93a30aa438..903648fd1b 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_asset_context.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_asset_context.py
@@ -10,7 +10,7 @@ from ayon_core.pipeline.publish import (
PublishXmlValidationError,
OptionalPyblishPluginMixin
)
-from ayon_core.hosts.nuke.api import SelectInstanceNodeAction
+from ayon_nuke.api import SelectInstanceNodeAction
class ValidateCorrectAssetContext(
@@ -34,6 +34,8 @@ class ValidateCorrectAssetContext(
]
optional = True
+ settings_category = "nuke"
+
@classmethod
def apply_settings(cls, project_settings):
"""Apply deprecated settings from project settings.
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_backdrop.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_backdrop.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_backdrop.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_backdrop.py
index 22344c661e..f7b94e0c82 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_backdrop.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_backdrop.py
@@ -1,6 +1,6 @@
import nuke
import pyblish
-from ayon_core.hosts.nuke import api as napi
+from ayon_nuke import api as napi
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
@@ -65,6 +65,8 @@ class ValidateBackdrop(
hosts = ["nuke"]
actions = [SelectCenterInNodeGraph]
+ settings_category = "nuke"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_exposed_knobs.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_exposed_knobs.py
index 217fe6fb85..d1b7c146fb 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_exposed_knobs.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_exposed_knobs.py
@@ -1,7 +1,7 @@
import pyblish.api
from ayon_core.pipeline.publish import get_errored_instances_from_context
-from ayon_core.hosts.nuke.api.lib import link_knobs
+from ayon_nuke.api.lib import link_knobs
from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin,
PublishValidationError
@@ -52,6 +52,9 @@ class ValidateExposedKnobs(
label = "Validate Exposed Knobs"
actions = [RepairExposedKnobs]
hosts = ["nuke"]
+
+ settings_category = "nuke"
+
product_types_mapping = {
"render": "CreateWriteRender",
"prerender": "CreateWritePrerender",
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_gizmo.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_gizmo.py
similarity index 97%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_gizmo.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_gizmo.py
index 2cdcb90d70..55249ae931 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_gizmo.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_gizmo.py
@@ -1,6 +1,6 @@
import pyblish
from ayon_core.pipeline import PublishXmlValidationError
-from ayon_core.hosts.nuke import api as napi
+from ayon_nuke import api as napi
import nuke
@@ -43,6 +43,8 @@ class ValidateGizmo(pyblish.api.InstancePlugin):
hosts = ["nuke"]
actions = [OpenFailedGroupNode]
+ settings_category = "nuke"
+
def process(self, instance):
grpn = instance.data["transientData"]["node"]
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_knobs.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_knobs.py
index 8bcde9609d..ea03bd94b2 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_knobs.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_knobs.py
@@ -32,6 +32,8 @@ class ValidateKnobs(pyblish.api.ContextPlugin):
actions = [RepairContextAction]
optional = True
+ settings_category = "nuke"
+
knobs = "{}"
def process(self, context):
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_output_resolution.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_output_resolution.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_output_resolution.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_output_resolution.py
index e8a00d2294..440cb8b758 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_output_resolution.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_output_resolution.py
@@ -1,6 +1,6 @@
import pyblish.api
-from ayon_core.hosts.nuke import api as napi
+from ayon_nuke import api as napi
from ayon_core.pipeline.publish import RepairAction
from ayon_core.pipeline import (
PublishXmlValidationError,
@@ -27,6 +27,8 @@ class ValidateOutputResolution(
hosts = ["nuke"]
actions = [RepairAction]
+ settings_category = "nuke"
+
missing_msg = "Missing Reformat node in render group node"
resolution_msg = "Reformat is set to wrong format"
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_proxy_mode.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_proxy_mode.py
similarity index 96%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_proxy_mode.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_proxy_mode.py
index 26e54295c9..1eb858b17e 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_proxy_mode.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_proxy_mode.py
@@ -25,6 +25,8 @@ class ValidateProxyMode(pyblish.api.ContextPlugin):
hosts = ["nuke"]
actions = [FixProxyMode]
+ settings_category = "nuke"
+
def process(self, context):
rootNode = nuke.root()
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_rendered_frames.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_rendered_frames.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_rendered_frames.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_rendered_frames.py
index 76ac7e97ad..20b7f6a6ac 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_rendered_frames.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_rendered_frames.py
@@ -54,6 +54,8 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin):
hosts = ["nuke", "nukestudio"]
actions = [RepairCollectionActionToLocal, RepairCollectionActionToFarm]
+ settings_category = "nuke"
+
def process(self, instance):
node = instance.data["transientData"]["node"]
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_script_attributes.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_script_attributes.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_script_attributes.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_script_attributes.py
index 2bd2034079..617d8d835b 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_script_attributes.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_script_attributes.py
@@ -5,7 +5,7 @@ from ayon_core.pipeline import (
OptionalPyblishPluginMixin
)
from ayon_core.pipeline.publish import RepairAction
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
WorkfileSettings
)
@@ -23,6 +23,8 @@ class ValidateScriptAttributes(
optional = True
actions = [RepairAction]
+ settings_category = "nuke"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_write_nodes.py
similarity index 98%
rename from client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py
rename to server_addon/nuke/client/ayon_nuke/plugins/publish/validate_write_nodes.py
index 0244c1d504..d642a4314c 100644
--- a/client/ayon_core/hosts/nuke/plugins/publish/validate_write_nodes.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/publish/validate_write_nodes.py
@@ -2,7 +2,7 @@ from collections import defaultdict
import pyblish.api
from ayon_core.pipeline.publish import get_errored_instances_from_context
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
get_write_node_template_attr,
set_node_knobs_from_settings,
color_gui_to_int
@@ -59,6 +59,8 @@ class ValidateNukeWriteNode(
actions = [RepairNukeWriteNodeAction]
hosts = ["nuke"]
+ settings_category = "nuke"
+
def process(self, instance):
if not self.is_active(instance.data):
return
diff --git a/client/ayon_core/hosts/nuke/plugins/workfile_build/create_placeholder.py b/server_addon/nuke/client/ayon_nuke/plugins/workfile_build/create_placeholder.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/workfile_build/create_placeholder.py
rename to server_addon/nuke/client/ayon_nuke/plugins/workfile_build/create_placeholder.py
index a5490021e4..4d43d59bad 100644
--- a/client/ayon_core/hosts/nuke/plugins/workfile_build/create_placeholder.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/workfile_build/create_placeholder.py
@@ -4,7 +4,7 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
CreatePlaceholderItem,
PlaceholderCreateMixin,
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
find_free_space_to_paste_nodes,
get_extreme_positions,
get_group_io_nodes,
@@ -18,7 +18,7 @@ from ayon_core.hosts.nuke.api.lib import (
duplicate_node,
node_tempfile,
)
-from ayon_core.hosts.nuke.api.workfile_template_builder import (
+from ayon_nuke.api.workfile_template_builder import (
NukePlaceholderPlugin
)
diff --git a/client/ayon_core/hosts/nuke/plugins/workfile_build/load_placeholder.py b/server_addon/nuke/client/ayon_nuke/plugins/workfile_build/load_placeholder.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/plugins/workfile_build/load_placeholder.py
rename to server_addon/nuke/client/ayon_nuke/plugins/workfile_build/load_placeholder.py
index 258f48c9d3..68bc10e41b 100644
--- a/client/ayon_core/hosts/nuke/plugins/workfile_build/load_placeholder.py
+++ b/server_addon/nuke/client/ayon_nuke/plugins/workfile_build/load_placeholder.py
@@ -4,7 +4,7 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
LoadPlaceholderItem,
PlaceholderLoadMixin,
)
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
find_free_space_to_paste_nodes,
get_extreme_positions,
get_group_io_nodes,
@@ -18,7 +18,7 @@ from ayon_core.hosts.nuke.api.lib import (
duplicate_node,
node_tempfile,
)
-from ayon_core.hosts.nuke.api.workfile_template_builder import (
+from ayon_nuke.api.workfile_template_builder import (
NukePlaceholderPlugin
)
diff --git a/client/ayon_core/hosts/nuke/plugins/create/__init__.py b/server_addon/nuke/client/ayon_nuke/startup/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/plugins/create/__init__.py
rename to server_addon/nuke/client/ayon_nuke/startup/__init__.py
diff --git a/client/ayon_core/hosts/nuke/startup/clear_rendered.py b/server_addon/nuke/client/ayon_nuke/startup/clear_rendered.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/startup/clear_rendered.py
rename to server_addon/nuke/client/ayon_nuke/startup/clear_rendered.py
diff --git a/client/ayon_core/hosts/nuke/startup/custom_write_node.py b/server_addon/nuke/client/ayon_nuke/startup/custom_write_node.py
similarity index 99%
rename from client/ayon_core/hosts/nuke/startup/custom_write_node.py
rename to server_addon/nuke/client/ayon_nuke/startup/custom_write_node.py
index f119e69919..5b0f240a49 100644
--- a/client/ayon_core/hosts/nuke/startup/custom_write_node.py
+++ b/server_addon/nuke/client/ayon_nuke/startup/custom_write_node.py
@@ -3,7 +3,7 @@ import os
import nuke
import nukescripts
from ayon_core.pipeline import Anatomy, get_current_project_name
-from ayon_core.hosts.nuke.api.lib import (
+from ayon_nuke.api.lib import (
set_node_knobs_from_settings,
get_nuke_imageio_settings
)
diff --git a/client/ayon_core/hosts/nuke/startup/frame_setting_for_read_nodes.py b/server_addon/nuke/client/ayon_nuke/startup/frame_setting_for_read_nodes.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/startup/frame_setting_for_read_nodes.py
rename to server_addon/nuke/client/ayon_nuke/startup/frame_setting_for_read_nodes.py
diff --git a/client/ayon_core/hosts/nuke/startup/menu.py b/server_addon/nuke/client/ayon_nuke/startup/menu.py
similarity index 64%
rename from client/ayon_core/hosts/nuke/startup/menu.py
rename to server_addon/nuke/client/ayon_nuke/startup/menu.py
index 2559e2142a..c3dd8cda8f 100644
--- a/client/ayon_core/hosts/nuke/startup/menu.py
+++ b/server_addon/nuke/client/ayon_nuke/startup/menu.py
@@ -1,5 +1,5 @@
from ayon_core.pipeline import install_host
-from ayon_core.hosts.nuke.api import NukeHost
+from ayon_nuke.api import NukeHost
host = NukeHost()
install_host(host)
diff --git a/client/ayon_core/hosts/nuke/startup/write_to_read.py b/server_addon/nuke/client/ayon_nuke/startup/write_to_read.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/startup/write_to_read.py
rename to server_addon/nuke/client/ayon_nuke/startup/write_to_read.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/__init__.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/__init__.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/__init__.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/any_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/any_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/any_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/any_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/api_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/api_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/api_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/api_pb2.py
diff --git a/client/ayon_core/hosts/nuke/startup/__init__.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/compiler/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/startup/__init__.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/compiler/__init__.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/compiler/plugin_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/compiler/plugin_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/compiler/plugin_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/compiler/plugin_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor_database.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor_database.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor_database.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor_database.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor_pool.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor_pool.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/descriptor_pool.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/descriptor_pool.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/duration_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/duration_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/duration_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/duration_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/empty_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/empty_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/empty_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/empty_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/field_mask_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/field_mask_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/field_mask_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/field_mask_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/compiler/__init__.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/compiler/__init__.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/__init__.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/_parameterized.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/_parameterized.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/_parameterized.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/_parameterized.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/api_implementation.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/api_implementation.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/api_implementation.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/api_implementation.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/builder.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/builder.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/builder.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/builder.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/containers.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/containers.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/containers.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/containers.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/decoder.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/decoder.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/decoder.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/decoder.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/encoder.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/encoder.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/encoder.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/encoder.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/enum_type_wrapper.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/enum_type_wrapper.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/enum_type_wrapper.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/enum_type_wrapper.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/extension_dict.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/extension_dict.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/extension_dict.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/extension_dict.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/message_listener.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/message_listener.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/message_listener.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/message_listener.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/message_set_extensions_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/message_set_extensions_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/message_set_extensions_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/message_set_extensions_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/missing_enum_values_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/missing_enum_values_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/missing_enum_values_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/missing_enum_values_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/more_extensions_dynamic_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/more_extensions_dynamic_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/more_extensions_dynamic_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/more_extensions_dynamic_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/more_extensions_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/more_extensions_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/more_extensions_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/more_extensions_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/more_messages_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/more_messages_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/more_messages_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/more_messages_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/no_package_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/no_package_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/no_package_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/no_package_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/python_message.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/python_message.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/python_message.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/python_message.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/type_checkers.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/type_checkers.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/type_checkers.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/type_checkers.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/well_known_types.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/well_known_types.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/well_known_types.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/well_known_types.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/wire_format.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/wire_format.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/wire_format.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/internal/wire_format.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/json_format.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/json_format.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/json_format.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/json_format.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/message.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/message.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/message.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/message.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/message_factory.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/message_factory.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/message_factory.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/message_factory.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/proto_builder.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/proto_builder.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/proto_builder.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/proto_builder.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/__init__.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/pyext/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/internal/__init__.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/pyext/__init__.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/pyext/cpp_message.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/pyext/cpp_message.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/pyext/cpp_message.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/pyext/cpp_message.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/pyext/python_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/pyext/python_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/pyext/python_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/pyext/python_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/reflection.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/reflection.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/reflection.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/reflection.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/service.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/service.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/service.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/service.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/service_reflection.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/service_reflection.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/service_reflection.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/service_reflection.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/source_context_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/source_context_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/source_context_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/source_context_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/struct_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/struct_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/struct_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/struct_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/symbol_database.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/symbol_database.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/symbol_database.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/symbol_database.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/text_encoding.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_encoding.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/text_encoding.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_encoding.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/text_format.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/text_format.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/timestamp_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/timestamp_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/timestamp_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/timestamp_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/type_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/type_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/type_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/type_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/pyext/__init__.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/util/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/pyext/__init__.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/util/__init__.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/util/json_format_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/util/json_format_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/util/json_format_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/util/json_format_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/util/json_format_proto3_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/util/json_format_proto3_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/util/json_format_proto3_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/util/json_format_proto3_pb2.py
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/wrappers_pb2.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/wrappers_pb2.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/wrappers_pb2.py
rename to server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/wrappers_pb2.py
diff --git a/server_addon/nuke/package.py b/server_addon/nuke/package.py
index bf03c4e7e7..9e1f9362cb 100644
--- a/server_addon/nuke/package.py
+++ b/server_addon/nuke/package.py
@@ -1,3 +1,10 @@
name = "nuke"
title = "Nuke"
-version = "0.1.11"
+version = "0.2.1"
+
+client_dir = "ayon_nuke"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/server_addon/nuke/server/settings/create_plugins.py b/server_addon/nuke/server/settings/create_plugins.py
index 6bdc5ee5ad..e4a0f9c938 100644
--- a/server_addon/nuke/server/settings/create_plugins.py
+++ b/server_addon/nuke/server/settings/create_plugins.py
@@ -12,7 +12,11 @@ def instance_attributes_enum():
return [
{"value": "reviewable", "label": "Reviewable"},
{"value": "farm_rendering", "label": "Farm rendering"},
- {"value": "use_range_limit", "label": "Use range limit"}
+ {"value": "use_range_limit", "label": "Use range limit"},
+ {
+ "value": "render_on_farm",
+ "label": "Render On Farm"
+ }
]
diff --git a/server_addon/nuke/server/settings/loader_plugins.py b/server_addon/nuke/server/settings/loader_plugins.py
index 531ea8d986..22cb469e8d 100644
--- a/server_addon/nuke/server/settings/loader_plugins.py
+++ b/server_addon/nuke/server/settings/loader_plugins.py
@@ -22,7 +22,9 @@ class LoadClipOptionsModel(BaseSettingsModel):
add_retime: bool = SettingsField(
title="Add retime"
)
-
+ deep_exr: bool = SettingsField(
+ title="Deep Exr Read Node"
+ )
class LoadClipModel(BaseSettingsModel):
enabled: bool = SettingsField(
@@ -65,7 +67,8 @@ DEFAULT_LOADER_PLUGINS_SETTINGS = {
"node_name_template": "{class_name}_{ext}",
"options_defaults": {
"start_at_workfile": True,
- "add_retime": True
+ "add_retime": True,
+ "deep_exr": False
}
}
}
diff --git a/server_addon/nuke/server/settings/publish_plugins.py b/server_addon/nuke/server/settings/publish_plugins.py
index d5b05d8715..6c37ecd37a 100644
--- a/server_addon/nuke/server/settings/publish_plugins.py
+++ b/server_addon/nuke/server/settings/publish_plugins.py
@@ -125,6 +125,7 @@ class ReformatNodesConfigModel(BaseSettingsModel):
class IntermediateOutputModel(BaseSettingsModel):
name: str = SettingsField(title="Output name")
+ publish: bool = SettingsField(title="Publish")
filter: BakingStreamFilterModel = SettingsField(
title="Filter", default_factory=BakingStreamFilterModel)
read_raw: bool = SettingsField(
@@ -230,10 +231,6 @@ class PublishPluginsModel(BaseSettingsModel):
default_factory=OptionalPluginModel,
section="Validators"
)
- ValidateContainers: OptionalPluginModel = SettingsField(
- title="Validate Containers",
- default_factory=OptionalPluginModel
- )
ValidateKnobs: ValidateKnobsModel = SettingsField(
title="Validate Knobs",
default_factory=ValidateKnobsModel
@@ -299,11 +296,6 @@ DEFAULT_PUBLISH_PLUGIN_SETTINGS = {
"optional": True,
"active": True
},
- "ValidateContainers": {
- "enabled": True,
- "optional": True,
- "active": True
- },
"ValidateKnobs": {
"enabled": False,
"knobs": "\n".join([
@@ -346,6 +338,7 @@ DEFAULT_PUBLISH_PLUGIN_SETTINGS = {
"outputs": [
{
"name": "baking",
+ "publish": False,
"filter": {
"task_types": [],
"product_types": [],
@@ -401,6 +394,7 @@ DEFAULT_PUBLISH_PLUGIN_SETTINGS = {
"outputs": [
{
"name": "baking",
+ "publish": False,
"filter": {
"task_types": [],
"product_types": [],
diff --git a/client/ayon_core/hosts/photoshop/__init__.py b/server_addon/photoshop/client/ayon_photoshop/__init__.py
similarity index 78%
rename from client/ayon_core/hosts/photoshop/__init__.py
rename to server_addon/photoshop/client/ayon_photoshop/__init__.py
index cf21b7df75..e72c79c812 100644
--- a/client/ayon_core/hosts/photoshop/__init__.py
+++ b/server_addon/photoshop/client/ayon_photoshop/__init__.py
@@ -1,3 +1,4 @@
+from .version import __version__
from .addon import (
PHOTOSHOP_ADDON_ROOT,
PhotoshopAddon,
@@ -6,6 +7,8 @@ from .addon import (
__all__ = (
+ "__version__",
+
"PHOTOSHOP_ADDON_ROOT",
"PhotoshopAddon",
"get_launch_script_path",
diff --git a/client/ayon_core/hosts/photoshop/addon.py b/server_addon/photoshop/client/ayon_photoshop/addon.py
similarity index 94%
rename from client/ayon_core/hosts/photoshop/addon.py
rename to server_addon/photoshop/client/ayon_photoshop/addon.py
index 65fe6a7cd1..d0fe638f15 100644
--- a/client/ayon_core/hosts/photoshop/addon.py
+++ b/server_addon/photoshop/client/ayon_photoshop/addon.py
@@ -1,11 +1,14 @@
import os
from ayon_core.addon import AYONAddon, IHostAddon
+from .version import __version__
+
PHOTOSHOP_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
class PhotoshopAddon(AYONAddon, IHostAddon):
name = "photoshop"
+ version = __version__
host_name = "photoshop"
def add_implementation_envs(self, env, _app):
@@ -33,4 +36,3 @@ def get_launch_script_path():
return os.path.join(
PHOTOSHOP_ADDON_ROOT, "api", "launch_script.py"
)
-
diff --git a/client/ayon_core/hosts/photoshop/api/README.md b/server_addon/photoshop/client/ayon_photoshop/api/README.md
similarity index 97%
rename from client/ayon_core/hosts/photoshop/api/README.md
rename to server_addon/photoshop/client/ayon_photoshop/api/README.md
index b391131a42..ef458dea16 100644
--- a/client/ayon_core/hosts/photoshop/api/README.md
+++ b/server_addon/photoshop/client/ayon_photoshop/api/README.md
@@ -17,7 +17,7 @@ ExManCmd /install {path to addon}/api/extension.zxp
The easiest way to get the server and Photoshop launch is with:
```
-python -c ^"import ayon_core.hosts.photoshop;ayon_core.hosts.photoshop.launch(""C:\Program Files\Adobe\Adobe Photoshop 2020\Photoshop.exe"")^"
+python -c ^"import ayon_photoshop;ayon_photoshop.launch(""C:\Program Files\Adobe\Adobe Photoshop 2020\Photoshop.exe"")^"
```
`avalon.photoshop.launch` launches the application and server, and also closes the server when Photoshop exists.
@@ -128,7 +128,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
import os
from ayon_core.pipeline import publish
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class ExtractImage(publish.Extractor):
diff --git a/client/ayon_core/hosts/photoshop/api/__init__.py b/server_addon/photoshop/client/ayon_photoshop/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/__init__.py
rename to server_addon/photoshop/client/ayon_photoshop/api/__init__.py
diff --git a/client/ayon_core/hosts/photoshop/api/extension.zxp b/server_addon/photoshop/client/ayon_photoshop/api/extension.zxp
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension.zxp
rename to server_addon/photoshop/client/ayon_photoshop/api/extension.zxp
diff --git a/client/ayon_core/hosts/photoshop/api/extension/.debug b/server_addon/photoshop/client/ayon_photoshop/api/extension/.debug
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/.debug
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/.debug
diff --git a/client/ayon_core/hosts/photoshop/api/extension/CSXS/manifest.xml b/server_addon/photoshop/client/ayon_photoshop/api/extension/CSXS/manifest.xml
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/CSXS/manifest.xml
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/CSXS/manifest.xml
diff --git a/client/ayon_core/hosts/photoshop/api/extension/client/CSInterface.js b/server_addon/photoshop/client/ayon_photoshop/api/extension/client/CSInterface.js
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/client/CSInterface.js
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/client/CSInterface.js
diff --git a/client/ayon_core/hosts/photoshop/api/extension/client/client.js b/server_addon/photoshop/client/ayon_photoshop/api/extension/client/client.js
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/client/client.js
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/client/client.js
diff --git a/client/ayon_core/hosts/photoshop/api/extension/client/loglevel.min.js b/server_addon/photoshop/client/ayon_photoshop/api/extension/client/loglevel.min.js
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/client/loglevel.min.js
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/client/loglevel.min.js
diff --git a/client/ayon_core/hosts/photoshop/api/extension/client/wsrpc.js b/server_addon/photoshop/client/ayon_photoshop/api/extension/client/wsrpc.js
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/client/wsrpc.js
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/client/wsrpc.js
diff --git a/client/ayon_core/hosts/photoshop/api/extension/client/wsrpc.min.js b/server_addon/photoshop/client/ayon_photoshop/api/extension/client/wsrpc.min.js
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/client/wsrpc.min.js
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/client/wsrpc.min.js
diff --git a/client/ayon_core/hosts/photoshop/api/extension/host/JSX.js b/server_addon/photoshop/client/ayon_photoshop/api/extension/host/JSX.js
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/host/JSX.js
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/host/JSX.js
diff --git a/client/ayon_core/hosts/photoshop/api/extension/host/index.jsx b/server_addon/photoshop/client/ayon_photoshop/api/extension/host/index.jsx
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/host/index.jsx
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/host/index.jsx
diff --git a/client/ayon_core/hosts/photoshop/api/extension/host/json.js b/server_addon/photoshop/client/ayon_photoshop/api/extension/host/json.js
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/host/json.js
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/host/json.js
diff --git a/client/ayon_core/hosts/photoshop/api/extension/icons/ayon_logo.png b/server_addon/photoshop/client/ayon_photoshop/api/extension/icons/ayon_logo.png
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/icons/ayon_logo.png
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/icons/ayon_logo.png
diff --git a/client/ayon_core/hosts/photoshop/api/extension/index.html b/server_addon/photoshop/client/ayon_photoshop/api/extension/index.html
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/extension/index.html
rename to server_addon/photoshop/client/ayon_photoshop/api/extension/index.html
diff --git a/client/ayon_core/hosts/photoshop/api/launch_logic.py b/server_addon/photoshop/client/ayon_photoshop/api/launch_logic.py
similarity index 99%
rename from client/ayon_core/hosts/photoshop/api/launch_logic.py
rename to server_addon/photoshop/client/ayon_photoshop/api/launch_logic.py
index c388f93044..04401a0972 100644
--- a/client/ayon_core/hosts/photoshop/api/launch_logic.py
+++ b/server_addon/photoshop/client/ayon_photoshop/api/launch_logic.py
@@ -22,9 +22,9 @@ from ayon_core.pipeline.workfile import (
)
from ayon_core.pipeline.template_data import get_template_data_with_names
from ayon_core.tools.utils import host_tools
-from ayon_core.tools.adobe_webserver.app import WebServerTool
from ayon_core.pipeline.context_tools import change_current_context
+from .webserver import WebServerTool
from .ws_stub import PhotoshopServerStub
log = Logger.get_logger(__name__)
diff --git a/client/ayon_core/hosts/photoshop/api/launch_script.py b/server_addon/photoshop/client/ayon_photoshop/api/launch_script.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/api/launch_script.py
rename to server_addon/photoshop/client/ayon_photoshop/api/launch_script.py
index bb4de80086..de7fc8ba48 100644
--- a/client/ayon_core/hosts/photoshop/api/launch_script.py
+++ b/server_addon/photoshop/client/ayon_photoshop/api/launch_script.py
@@ -8,7 +8,7 @@ workfile or others.
import os
import sys
-from ayon_core.hosts.photoshop.api.lib import main as host_main
+from ayon_photoshop.api.lib import main as host_main
# Get current file to locate start point of sys.argv
CURRENT_FILE = os.path.abspath(__file__)
diff --git a/client/ayon_core/hosts/photoshop/api/lib.py b/server_addon/photoshop/client/ayon_photoshop/api/lib.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/api/lib.py
rename to server_addon/photoshop/client/ayon_photoshop/api/lib.py
index af14e6d02f..fd003919ce 100644
--- a/client/ayon_core/hosts/photoshop/api/lib.py
+++ b/server_addon/photoshop/client/ayon_photoshop/api/lib.py
@@ -19,7 +19,7 @@ def safe_excepthook(*args):
def main(*subprocess_args):
- from ayon_core.hosts.photoshop.api import PhotoshopHost
+ from ayon_photoshop.api import PhotoshopHost
host = PhotoshopHost()
install_host(host)
diff --git a/client/ayon_core/hosts/photoshop/api/panel.png b/server_addon/photoshop/client/ayon_photoshop/api/panel.png
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/panel.png
rename to server_addon/photoshop/client/ayon_photoshop/api/panel.png
diff --git a/client/ayon_core/hosts/photoshop/api/panel_failure.png b/server_addon/photoshop/client/ayon_photoshop/api/panel_failure.png
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/panel_failure.png
rename to server_addon/photoshop/client/ayon_photoshop/api/panel_failure.png
diff --git a/client/ayon_core/hosts/photoshop/api/pipeline.py b/server_addon/photoshop/client/ayon_photoshop/api/pipeline.py
similarity index 99%
rename from client/ayon_core/hosts/photoshop/api/pipeline.py
rename to server_addon/photoshop/client/ayon_photoshop/api/pipeline.py
index 27cfa5a7b5..d399bb25e2 100644
--- a/client/ayon_core/hosts/photoshop/api/pipeline.py
+++ b/server_addon/photoshop/client/ayon_photoshop/api/pipeline.py
@@ -21,8 +21,8 @@ from ayon_core.host import (
)
from ayon_core.pipeline.load import any_outdated_containers
-from ayon_core.hosts.photoshop import PHOTOSHOP_ADDON_ROOT
from ayon_core.tools.utils import get_ayon_qt_app
+from ayon_photoshop import PHOTOSHOP_ADDON_ROOT
from . import lib
diff --git a/client/ayon_core/hosts/photoshop/api/plugin.py b/server_addon/photoshop/client/ayon_photoshop/api/plugin.py
similarity index 100%
rename from client/ayon_core/hosts/photoshop/api/plugin.py
rename to server_addon/photoshop/client/ayon_photoshop/api/plugin.py
diff --git a/server_addon/photoshop/client/ayon_photoshop/api/webserver.py b/server_addon/photoshop/client/ayon_photoshop/api/webserver.py
new file mode 100644
index 0000000000..cd229c65ad
--- /dev/null
+++ b/server_addon/photoshop/client/ayon_photoshop/api/webserver.py
@@ -0,0 +1,241 @@
+"""Webserver for communication with photoshop.
+
+Aiohttp (Asyncio) based websocket server used for communication with host
+application.
+
+This webserver is started in spawned Python process that opens DCC during
+its launch, waits for connection from DCC and handles communication going
+forward. Server is closed before Python process is killed.
+"""
+import os
+import logging
+import urllib
+import threading
+import asyncio
+import socket
+
+from aiohttp import web
+
+from wsrpc_aiohttp import WSRPCClient
+
+from ayon_core.pipeline import get_global_context
+
+log = logging.getLogger(__name__)
+
+
+class WebServerTool:
+ """
+ Basic POC implementation of asychronic websocket RPC server.
+ Uses class in external_app_1.py to mimic implementation for single
+ external application.
+ 'test_client' folder contains two test implementations of client
+ """
+ _instance = None
+
+ def __init__(self):
+ WebServerTool._instance = self
+
+ self.client = None
+ self.handlers = {}
+ self.on_stop_callbacks = []
+
+ port = None
+ host_name = "localhost"
+ websocket_url = os.getenv("WEBSOCKET_URL")
+ if websocket_url:
+ parsed = urllib.parse.urlparse(websocket_url)
+ port = parsed.port
+ host_name = parsed.netloc.split(":")[0]
+ if not port:
+ port = 8098 # fallback
+
+ self.port = port
+ self.host_name = host_name
+
+ self.app = web.Application()
+
+ # add route with multiple methods for single "external app"
+ self.webserver_thread = WebServerThread(self, self.port)
+
+ def add_route(self, *args, **kwargs):
+ self.app.router.add_route(*args, **kwargs)
+
+ def add_static(self, *args, **kwargs):
+ self.app.router.add_static(*args, **kwargs)
+
+ def start_server(self):
+ if self.webserver_thread and not self.webserver_thread.is_alive():
+ self.webserver_thread.start()
+
+ def stop_server(self):
+ self.stop()
+
+ async def send_context_change(self, host):
+ """
+ Calls running webserver to inform about context change
+
+ Used when new PS/AE should be triggered,
+ but one already running, without
+ this publish would point to old context.
+ """
+ client = WSRPCClient(os.getenv("WEBSOCKET_URL"),
+ loop=asyncio.get_event_loop())
+ await client.connect()
+
+ context = get_global_context()
+ project_name = context["project_name"]
+ folder_path = context["folder_path"]
+ task_name = context["task_name"]
+ log.info("Sending context change to {}{}/{}".format(
+ project_name, folder_path, task_name
+ ))
+
+ await client.call(
+ '{}.set_context'.format(host),
+ project=project_name,
+ folder=folder_path,
+ task=task_name
+ )
+ await client.close()
+
+ def port_occupied(self, host_name, port):
+ """
+ Check if 'url' is already occupied.
+
+ This could mean, that app is already running and we are trying open it
+ again. In that case, use existing running webserver.
+ Check here is easier than capturing exception from thread.
+ """
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as con:
+ result = con.connect_ex((host_name, port)) == 0
+
+ if result:
+ print(f"Port {port} is already in use")
+ return result
+
+ def call(self, func):
+ log.debug("websocket.call {}".format(func))
+ future = asyncio.run_coroutine_threadsafe(
+ func,
+ self.webserver_thread.loop
+ )
+ result = future.result()
+ return result
+
+ @staticmethod
+ def get_instance():
+ if WebServerTool._instance is None:
+ WebServerTool()
+ return WebServerTool._instance
+
+ @property
+ def is_running(self):
+ if not self.webserver_thread:
+ return False
+ return self.webserver_thread.is_running
+
+ def stop(self):
+ if not self.is_running:
+ return
+ try:
+ log.debug("Stopping websocket server")
+ self.webserver_thread.is_running = False
+ self.webserver_thread.stop()
+ except Exception:
+ log.warning(
+ "Error has happened during Killing websocket server",
+ exc_info=True
+ )
+
+ def thread_stopped(self):
+ for callback in self.on_stop_callbacks:
+ callback()
+
+
+class WebServerThread(threading.Thread):
+ """ Listener for websocket rpc requests.
+
+ It would be probably better to "attach" this to main thread (as for
+ example Harmony needs to run something on main thread), but currently
+ it creates separate thread and separate asyncio event loop
+ """
+ def __init__(self, module, port):
+ super(WebServerThread, self).__init__()
+
+ self.is_running = False
+ self.port = port
+ self.module = module
+ self.loop = None
+ self.runner = None
+ self.site = None
+ self.tasks = []
+
+ def run(self):
+ self.is_running = True
+
+ try:
+ log.info("Starting web server")
+ self.loop = asyncio.new_event_loop() # create new loop for thread
+ asyncio.set_event_loop(self.loop)
+
+ self.loop.run_until_complete(self.start_server())
+
+ websocket_url = "ws://localhost:{}/ws".format(self.port)
+
+ log.debug(
+ "Running Websocket server on URL: \"{}\"".format(websocket_url)
+ )
+
+ asyncio.ensure_future(self.check_shutdown(), loop=self.loop)
+ self.loop.run_forever()
+ except Exception:
+ self.is_running = False
+ log.warning(
+ "Websocket Server service has failed", exc_info=True
+ )
+ raise
+ finally:
+ self.loop.close() # optional
+
+ self.is_running = False
+ self.module.thread_stopped()
+ log.info("Websocket server stopped")
+
+ async def start_server(self):
+ """ Starts runner and TCPsite """
+ self.runner = web.AppRunner(self.module.app)
+ await self.runner.setup()
+ self.site = web.TCPSite(self.runner, 'localhost', self.port)
+ await self.site.start()
+
+ def stop(self):
+ """Sets is_running flag to false, 'check_shutdown' shuts server down"""
+ self.is_running = False
+
+ async def check_shutdown(self):
+ """ Future that is running and checks if server should be running
+ periodically.
+ """
+ while self.is_running:
+ while self.tasks:
+ task = self.tasks.pop(0)
+ log.debug("waiting for task {}".format(task))
+ await task
+ log.debug("returned value {}".format(task.result))
+
+ await asyncio.sleep(0.5)
+
+ log.debug("Starting shutdown")
+ await self.site.stop()
+ log.debug("Site stopped")
+ await self.runner.cleanup()
+ log.debug("Runner stopped")
+ tasks = [task for task in asyncio.all_tasks() if
+ task is not asyncio.current_task()]
+ list(map(lambda task: task.cancel(), tasks)) # cancel all the tasks
+ results = await asyncio.gather(*tasks, return_exceptions=True)
+ log.debug(f'Finished awaiting cancelled tasks, results: {results}...')
+ await self.loop.shutdown_asyncgens()
+ # to really make sure everything else has time to stop
+ await asyncio.sleep(0.07)
+ self.loop.stop()
diff --git a/client/ayon_core/hosts/photoshop/api/ws_stub.py b/server_addon/photoshop/client/ayon_photoshop/api/ws_stub.py
similarity index 99%
rename from client/ayon_core/hosts/photoshop/api/ws_stub.py
rename to server_addon/photoshop/client/ayon_photoshop/api/ws_stub.py
index 36fe0af2f8..3619fa4b7a 100644
--- a/client/ayon_core/hosts/photoshop/api/ws_stub.py
+++ b/server_addon/photoshop/client/ayon_photoshop/api/ws_stub.py
@@ -6,7 +6,7 @@ import json
import attr
from wsrpc_aiohttp import WebSocketAsync
-from ayon_core.tools.adobe_webserver.app import WebServerTool
+from .webserver import WebServerTool
@attr.s
diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/server_addon/photoshop/client/ayon_photoshop/hooks/pre_launch_args.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py
rename to server_addon/photoshop/client/ayon_photoshop/hooks/pre_launch_args.py
index 70f8fc730f..ff60c2f40d 100644
--- a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py
+++ b/server_addon/photoshop/client/ayon_photoshop/hooks/pre_launch_args.py
@@ -7,7 +7,7 @@ from ayon_core.lib import (
is_using_ayon_console,
)
from ayon_applications import PreLaunchHook, LaunchTypes
-from ayon_core.hosts.photoshop import get_launch_script_path
+from ayon_photoshop import get_launch_script_path
def get_launch_kwargs(kwargs):
diff --git a/client/ayon_core/hosts/photoshop/lib.py b/server_addon/photoshop/client/ayon_photoshop/lib.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/lib.py
rename to server_addon/photoshop/client/ayon_photoshop/lib.py
index dd227c5d81..9dc90953c5 100644
--- a/client/ayon_core/hosts/photoshop/lib.py
+++ b/server_addon/photoshop/client/ayon_photoshop/lib.py
@@ -2,13 +2,13 @@ import re
import ayon_api
-import ayon_core.hosts.photoshop.api as api
from ayon_core.lib import prepare_template_data
from ayon_core.pipeline import (
AutoCreator,
CreatedInstance
)
-from ayon_core.hosts.photoshop.api.pipeline import cache_and_get_instances
+from ayon_photoshop import api
+from ayon_photoshop.api.pipeline import cache_and_get_instances
class PSAutoCreator(AutoCreator):
diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_flatten_image.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/create/create_flatten_image.py
index a3bc77c640..a467a5ecaa 100644
--- a/client/ayon_core/hosts/photoshop/plugins/create/create_flatten_image.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_flatten_image.py
@@ -1,7 +1,7 @@
import ayon_api
-import ayon_core.hosts.photoshop.api as api
-from ayon_core.hosts.photoshop.lib import PSAutoCreator, clean_product_name
+from ayon_photoshop import api
+from ayon_photoshop.lib import PSAutoCreator, clean_product_name
from ayon_core.lib import BoolDef, prepare_template_data
from ayon_core.pipeline.create import get_product_name, CreatedInstance
diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_image.py
similarity index 95%
rename from client/ayon_core/hosts/photoshop/plugins/create/create_image.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/create/create_image.py
index 26f2469844..0170306301 100644
--- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_image.py
@@ -1,6 +1,5 @@
import re
-from ayon_core.hosts.photoshop import api
from ayon_core.lib import BoolDef
from ayon_core.pipeline import (
Creator,
@@ -9,8 +8,9 @@ from ayon_core.pipeline import (
)
from ayon_core.lib import prepare_template_data
from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS
-from ayon_core.hosts.photoshop.api.pipeline import cache_and_get_instances
-from ayon_core.hosts.photoshop.lib import clean_product_name
+from ayon_photoshop import api
+from ayon_photoshop.api.pipeline import cache_and_get_instances
+from ayon_photoshop.lib import clean_product_name
class ImageCreator(Creator):
@@ -35,8 +35,12 @@ class ImageCreator(Creator):
create_empty_group = False
stub = api.stub() # only after PS is up
- top_level_selected_items = stub.get_selected_layers()
if pre_create_data.get("use_selection"):
+ try:
+ top_level_selected_items = stub.get_selected_layers()
+ except ValueError:
+ raise CreatorError("Cannot group locked Background layer!")
+
only_single_item_selected = len(top_level_selected_items) == 1
if (
only_single_item_selected or
@@ -50,11 +54,12 @@ class ImageCreator(Creator):
group = stub.group_selected_layers(product_name_from_ui)
groups_to_create.append(group)
else:
- stub.select_layers(stub.get_layers())
try:
+ stub.select_layers(stub.get_layers())
group = stub.group_selected_layers(product_name_from_ui)
- except:
+ except ValueError:
raise CreatorError("Cannot group locked Background layer!")
+
groups_to_create.append(group)
# create empty group if nothing selected
diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_review.py b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_review.py
similarity index 94%
rename from client/ayon_core/hosts/photoshop/plugins/create/create_review.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/create/create_review.py
index 229b736801..60c64b3831 100644
--- a/client/ayon_core/hosts/photoshop/plugins/create/create_review.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_review.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.photoshop.lib import PSAutoCreator
+from ayon_photoshop.lib import PSAutoCreator
class ReviewCreator(PSAutoCreator):
diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_workfile.py b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_workfile.py
similarity index 94%
rename from client/ayon_core/hosts/photoshop/plugins/create/create_workfile.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/create/create_workfile.py
index da0c9d1d12..ce44a1ad2d 100644
--- a/client/ayon_core/hosts/photoshop/plugins/create/create_workfile.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/create/create_workfile.py
@@ -1,4 +1,4 @@
-from ayon_core.hosts.photoshop.lib import PSAutoCreator
+from ayon_photoshop.lib import PSAutoCreator
class WorkfileCreator(PSAutoCreator):
diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py b/server_addon/photoshop/client/ayon_photoshop/plugins/load/load_image.py
similarity index 95%
rename from client/ayon_core/hosts/photoshop/plugins/load/load_image.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/load/load_image.py
index d71067615e..e3d80f6957 100644
--- a/client/ayon_core/hosts/photoshop/plugins/load/load_image.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/load/load_image.py
@@ -1,8 +1,8 @@
import re
from ayon_core.pipeline import get_representation_path
-from ayon_core.hosts.photoshop import api as photoshop
-from ayon_core.hosts.photoshop.api import get_unique_layer_name
+from ayon_photoshop import api as photoshop
+from ayon_photoshop.api import get_unique_layer_name
class ImageLoader(photoshop.PhotoshopLoader):
diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py b/server_addon/photoshop/client/ayon_photoshop/plugins/load/load_image_from_sequence.py
similarity index 95%
rename from client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/load/load_image_from_sequence.py
index dd14543f3e..f69dce26f6 100644
--- a/client/ayon_core/hosts/photoshop/plugins/load/load_image_from_sequence.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/load/load_image_from_sequence.py
@@ -2,8 +2,8 @@ import os
import qargparse
-from ayon_core.hosts.photoshop import api as photoshop
-from ayon_core.hosts.photoshop.api import get_unique_layer_name
+from ayon_photoshop import api as photoshop
+from ayon_photoshop.api import get_unique_layer_name
class ImageFromSequenceLoader(photoshop.PhotoshopLoader):
diff --git a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py b/server_addon/photoshop/client/ayon_photoshop/plugins/load/load_reference.py
similarity index 95%
rename from client/ayon_core/hosts/photoshop/plugins/load/load_reference.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/load/load_reference.py
index b563faff82..21076f6a4f 100644
--- a/client/ayon_core/hosts/photoshop/plugins/load/load_reference.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/load/load_reference.py
@@ -1,8 +1,8 @@
import re
from ayon_core.pipeline import get_representation_path
-from ayon_core.hosts.photoshop import api as photoshop
-from ayon_core.hosts.photoshop.api import get_unique_layer_name
+from ayon_photoshop import api as photoshop
+from ayon_photoshop.api import get_unique_layer_name
class ReferenceLoader(photoshop.PhotoshopLoader):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/closePS.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/closePS.py
similarity index 91%
rename from client/ayon_core/hosts/photoshop/plugins/publish/closePS.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/closePS.py
index 68c3b5b249..2cdc9fa1e8 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/closePS.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/closePS.py
@@ -2,7 +2,7 @@
"""Close PS after publish. For Webpublishing only."""
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class ClosePS(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_image.py
similarity index 98%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_image.py
index adbe02eb74..23a71bdf46 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_image.py
@@ -1,6 +1,6 @@
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
from ayon_core.pipeline.create import get_product_name
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image_refresh.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_image_refresh.py
similarity index 94%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image_refresh.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_image_refresh.py
index 7a5f297c89..108b65232a 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_image_refresh.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_image_refresh.py
@@ -1,6 +1,6 @@
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class CollectAutoImageRefresh(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_review.py
similarity index 98%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_review.py
index d7267d253a..8b84e69309 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_review.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_review.py
@@ -7,7 +7,7 @@ Provides:
"""
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
from ayon_core.pipeline.create import get_product_name
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_workfile.py
similarity index 98%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_workfile.py
index af74c76a15..1bf7c1a600 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_auto_workfile.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_auto_workfile.py
@@ -1,7 +1,7 @@
import os
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
from ayon_core.pipeline.create import get_product_name
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_batch_data.py
similarity index 100%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_batch_data.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_batch_data.py
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_color_coded_instances.py
similarity index 99%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_color_coded_instances.py
index e8f7c7e3df..072eb82179 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_color_coded_instances.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_color_coded_instances.py
@@ -4,8 +4,8 @@ import re
import pyblish.api
from ayon_core.lib import prepare_template_data, is_in_tests
-from ayon_core.hosts.photoshop import api as photoshop
from ayon_core.settings import get_project_settings
+from ayon_photoshop import api as photoshop
class CollectColorCodedInstances(pyblish.api.ContextPlugin):
@@ -33,6 +33,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin):
order = pyblish.api.CollectorOrder
hosts = ["photoshop"]
targets = ["automated"]
+ settings_category = "photoshop"
# configurable by Settings
color_code_mapping = []
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_current_file.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_current_file.py
similarity index 88%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_current_file.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_current_file.py
index 74353d452f..02f2217f75 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_current_file.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_current_file.py
@@ -2,7 +2,7 @@ import os
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class CollectCurrentFile(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_extension_version.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_extension_version.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_extension_version.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_extension_version.py
index 2d24a8de15..90415e9245 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_extension_version.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_extension_version.py
@@ -2,7 +2,7 @@ import os
import re
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class CollectExtensionVersion(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_image.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_image.py
similarity index 91%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_image.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_image.py
index bfd73bfc5f..ed6af6f7d3 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_image.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_image.py
@@ -1,6 +1,6 @@
import pyblish.api
-from ayon_core.hosts.photoshop import api
+from ayon_photoshop import api
class CollectImage(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_published_version.py
similarity index 100%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_published_version.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_published_version.py
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_review.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_review.py
similarity index 94%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_review.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_review.py
index 1ffbadf022..d9a29f9b74 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_review.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_review.py
@@ -16,6 +16,7 @@ class CollectReview(pyblish.api.ContextPlugin):
label = "Collect Review"
hosts = ["photoshop"]
order = pyblish.api.CollectorOrder + 0.1
+ settings_category = "photoshop"
def process(self, context):
for instance in context:
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_version.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_version.py
similarity index 96%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_version.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_version.py
index cda71d8643..bc9f05ab50 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/collect_version.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_version.py
@@ -22,6 +22,7 @@ class CollectVersion(pyblish.api.InstancePlugin):
hosts = ["photoshop"]
families = ["image", "review", "workfile"]
+ settings_category = "photoshop"
def process(self, instance):
workfile_version = instance.context.data["version"]
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_workfile.py
similarity index 100%
rename from client/ayon_core/hosts/photoshop/plugins/publish/collect_workfile.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/collect_workfile.py
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/extract_image.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_image.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/plugins/publish/extract_image.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_image.py
index 7290a1437e..33599d37bb 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/extract_image.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_image.py
@@ -2,7 +2,7 @@ import os
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class ExtractImage(pyblish.api.ContextPlugin):
@@ -22,6 +22,7 @@ class ExtractImage(pyblish.api.ContextPlugin):
families = ["image", "background"]
formats = ["png", "jpg"]
+ settings_category = "photoshop"
def process(self, context):
stub = photoshop.stub()
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/extract_review.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_review.py
similarity index 99%
rename from client/ayon_core/hosts/photoshop/plugins/publish/extract_review.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_review.py
index 3497e7ad75..0f36d31648 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/extract_review.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_review.py
@@ -7,7 +7,7 @@ from ayon_core.lib import (
get_ffmpeg_tool_args,
)
from ayon_core.pipeline import publish
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class ExtractReview(publish.Extractor):
@@ -29,6 +29,7 @@ class ExtractReview(publish.Extractor):
label = "Extract Review"
hosts = ["photoshop"]
families = ["review"]
+ settings_category = "photoshop"
# Extract Options
jpg_options = None
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/extract_save_scene.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_save_scene.py
similarity index 85%
rename from client/ayon_core/hosts/photoshop/plugins/publish/extract_save_scene.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_save_scene.py
index 962c0722db..22ebbb739d 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/extract_save_scene.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/extract_save_scene.py
@@ -1,5 +1,5 @@
from ayon_core.pipeline import publish
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class ExtractSaveScene(publish.Extractor):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/help/validate_instance_asset.xml
similarity index 100%
rename from client/ayon_core/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/help/validate_instance_asset.xml
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/help/validate_naming.xml
similarity index 100%
rename from client/ayon_core/hosts/photoshop/plugins/publish/help/validate_naming.xml
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/help/validate_naming.xml
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/increment_workfile.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/increment_workfile.py
similarity index 94%
rename from client/ayon_core/hosts/photoshop/plugins/publish/increment_workfile.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/increment_workfile.py
index 9b25a35ef5..b10645813a 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/increment_workfile.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/increment_workfile.py
@@ -3,7 +3,7 @@ import pyblish.api
from ayon_core.pipeline.publish import get_errored_plugins_from_context
from ayon_core.lib import version_up
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class IncrementWorkfile(pyblish.api.InstancePlugin):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/validate_instance_asset.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/validate_instance_asset.py
index c3a6822f32..36ba621dc2 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_instance_asset.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/validate_instance_asset.py
@@ -6,7 +6,7 @@ from ayon_core.pipeline.publish import (
PublishXmlValidationError,
OptionalPyblishPluginMixin
)
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
class ValidateInstanceFolderRepair(pyblish.api.Action):
diff --git a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/validate_naming.py
similarity index 97%
rename from client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py
rename to server_addon/photoshop/client/ayon_photoshop/plugins/publish/validate_naming.py
index 13c6a54fd2..e5f826b07e 100644
--- a/client/ayon_core/hosts/photoshop/plugins/publish/validate_naming.py
+++ b/server_addon/photoshop/client/ayon_photoshop/plugins/publish/validate_naming.py
@@ -2,7 +2,7 @@ import re
import pyblish.api
-from ayon_core.hosts.photoshop import api as photoshop
+from ayon_photoshop import api as photoshop
from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
@@ -16,6 +16,7 @@ class ValidateNamingRepair(pyblish.api.Action):
label = "Repair"
icon = "wrench"
on = "failed"
+ settings_category = "photoshop"
def process(self, context, plugin):
diff --git a/client/ayon_core/hosts/photoshop/resources/template.psd b/server_addon/photoshop/client/ayon_photoshop/resources/template.psd
similarity index 100%
rename from client/ayon_core/hosts/photoshop/resources/template.psd
rename to server_addon/photoshop/client/ayon_photoshop/resources/template.psd
diff --git a/server_addon/photoshop/client/ayon_photoshop/version.py b/server_addon/photoshop/client/ayon_photoshop/version.py
new file mode 100644
index 0000000000..31a969addf
--- /dev/null
+++ b/server_addon/photoshop/client/ayon_photoshop/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring AYON addon 'photoshop' version."""
+__version__ = "0.2.1"
diff --git a/server_addon/photoshop/client/pyproject.toml b/server_addon/photoshop/client/pyproject.toml
new file mode 100644
index 0000000000..3beb76ba74
--- /dev/null
+++ b/server_addon/photoshop/client/pyproject.toml
@@ -0,0 +1,6 @@
+[project]
+name="photoshop"
+description="AYON Phostoshop addon."
+
+[ayon.runtimeDependencies]
+wsrpc_aiohttp = "^3.1.1" # websocket server
diff --git a/server_addon/photoshop/package.py b/server_addon/photoshop/package.py
index 25615529d1..d04973ebc3 100644
--- a/server_addon/photoshop/package.py
+++ b/server_addon/photoshop/package.py
@@ -1,3 +1,10 @@
name = "photoshop"
title = "Photoshop"
-version = "0.1.2"
+version = "0.2.1"
+
+client_dir = "ayon_photoshop"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/server_addon/photoshop/server/settings/publish_plugins.py b/server_addon/photoshop/server/settings/publish_plugins.py
index d04faaf53a..149b08beb4 100644
--- a/server_addon/photoshop/server/settings/publish_plugins.py
+++ b/server_addon/photoshop/server/settings/publish_plugins.py
@@ -83,14 +83,6 @@ class CollectVersionPlugin(BaseSettingsModel):
enabled: bool = SettingsField(True, title="Enabled")
-class ValidateContainersPlugin(BaseSettingsModel):
- """Check that workfile contains latest version of loaded items""" # noqa
- _isGroup = True
- enabled: bool = True
- optional: bool = SettingsField(False, title="Optional")
- active: bool = SettingsField(True, title="Active")
-
-
class ValidateNamingPlugin(BaseSettingsModel):
"""Validate naming of products and layers""" # noqa
invalid_chars: str = SettingsField(
@@ -154,11 +146,6 @@ class PhotoshopPublishPlugins(BaseSettingsModel):
default_factory=CollectVersionPlugin,
)
- ValidateContainers: ValidateContainersPlugin = SettingsField(
- title="Validate Containers",
- default_factory=ValidateContainersPlugin,
- )
-
ValidateNaming: ValidateNamingPlugin = SettingsField(
title="Validate naming of products and layers",
default_factory=ValidateNamingPlugin,
@@ -187,11 +174,6 @@ DEFAULT_PUBLISH_SETTINGS = {
"CollectVersion": {
"enabled": False
},
- "ValidateContainers": {
- "enabled": True,
- "optional": True,
- "active": True
- },
"ValidateNaming": {
"invalid_chars": "[ \\\\/+\\*\\?\\(\\)\\[\\]\\{\\}:,;]",
"replace_char": "_"
diff --git a/client/ayon_core/hosts/resolve/README.markdown b/server_addon/resolve/client/ayon_resolve/README.markdown
similarity index 100%
rename from client/ayon_core/hosts/resolve/README.markdown
rename to server_addon/resolve/client/ayon_resolve/README.markdown
diff --git a/client/ayon_core/hosts/resolve/RESOLVE_API_v18.5.1-build6.txt b/server_addon/resolve/client/ayon_resolve/RESOLVE_API_v19.0B-build20.txt
similarity index 86%
rename from client/ayon_core/hosts/resolve/RESOLVE_API_v18.5.1-build6.txt
rename to server_addon/resolve/client/ayon_resolve/RESOLVE_API_v19.0B-build20.txt
index 7d1d6edf61..a2f3fa6f73 100644
--- a/client/ayon_core/hosts/resolve/RESOLVE_API_v18.5.1-build6.txt
+++ b/server_addon/resolve/client/ayon_resolve/RESOLVE_API_v19.0B-build20.txt
@@ -1,26 +1,23 @@
-Updated as of 26 May 2023
+Last Updated: 1 April 2024
----------------------------
In this package, you will find a brief introduction to the Scripting API for DaVinci Resolve Studio. Apart from this README.txt file, this package contains folders containing the basic import
modules for scripting access (DaVinciResolve.py) and some representative examples.
From v16.2.0 onwards, the nodeIndex parameters accepted by SetLUT() and SetCDL() are 1-based instead of 0-based, i.e. 1 <= nodeIndex <= total number of nodes.
-
Overview
--------
-As with Blackmagic Design Fusion scripts, user scripts written in Lua and Python programming languages are supported. By default, scripts can be invoked from the Console window in the Fusion page,
+As with Blackmagic Fusion scripts, user scripts written in Lua and Python programming languages are supported. By default, scripts can be invoked from the Console window in the Fusion page,
or via command line. This permission can be changed in Resolve Preferences, to be only from Console, or to be invoked from the local network. Please be aware of the security implications when
allowing scripting access from outside of the Resolve application.
-
Prerequisites
-------------
DaVinci Resolve scripting requires one of the following to be installed (for all users):
Lua 5.1
- Python 2.7 64-bit
Python >= 3.6 64-bit
-
+ Python 2.7 64-bit
Using a script
--------------
@@ -64,6 +61,7 @@ The interactive Console window allows for an easy way to execute simple scriptin
and Lua and evaluates and executes them immediately. For more information on how to use the Console, please refer to the DaVinci Resolve User Manual.
This example Python script creates a simple project:
+
#!/usr/bin/env python
import DaVinciResolveScript as dvr_script
resolve = dvr_script.scriptapp("Resolve")
@@ -80,9 +78,8 @@ Running DaVinci Resolve in headless mode
DaVinci Resolve can be launched in a headless mode without the user interface using the -nogui command line option. When DaVinci Resolve is launched using this option, the user interface is disabled.
However, the various scripting APIs will continue to work as expected.
-
-Basic Resolve API
------------------
+DaVinci Resolve API
+-------------------
Some commonly used API functions are described below (*). As with the resolve object, each object is inspectable for properties and functions.
Resolve
@@ -101,6 +98,12 @@ Resolve
SaveLayoutPreset(presetName) --> Bool # Saves current UI layout as a preset named 'presetName'.
ImportLayoutPreset(presetFilePath, presetName) --> Bool # Imports preset from path 'presetFilePath'. The optional argument 'presetName' specifies how the preset shall be named. If not specified, the preset is named based on the filename.
Quit() --> None # Quits the Resolve App.
+ ImportRenderPreset(presetPath) --> Bool # Import a preset from presetPath (string) and set it as current preset for rendering.
+ ExportRenderPreset(presetName, exportPath) --> Bool # Export a preset to a given path (string) if presetName(string) exists.
+ ImportBurnInPreset(presetPath) --> Bool # Import a data burn in preset from a given presetPath (string)
+ ExportBurnInPreset(presetName, exportPath) --> Bool # Export a data burn in preset to a given path (string) if presetName (string) exists.
+ GetKeyframeMode() --> keyframeMode # Returns the currently set keyframe mode (int). Refer to section 'Keyframe Mode information' below for details.
+ SetKeyframeMode(keyframeMode) --> Bool # Returns True when 'keyframeMode'(enum) is successfully set. Refer to section 'Keyframe Mode information' below for details.
ProjectManager
ArchiveProject(projectName,
@@ -131,6 +134,14 @@ ProjectManager
# 'DbType': 'Disk' or 'PostgreSQL' (string)
# 'DbName': database name (string)
# 'IpAddress': IP address of the PostgreSQL server (string, optional key - defaults to '127.0.0.1')
+ CreateCloudProject({cloudSettings}) --> Project # Creates and returns a cloud project.
+ # '{cloudSettings}': Check 'Cloud Projects Settings' subsection below for more information.
+ ImportCloudProject(filePath, {cloudSettings}) --> Bool # Returns True if import cloud project is successful; False otherwise
+ # 'filePath': String; filePath of file to import
+ # '{cloudSettings}': Check 'Cloud Projects Settings' subsection below for more information.
+ RestoreCloudProject(folderPath, {cloudSettings}) --> Bool # Returns True if restore cloud project is successful; False otherwise
+ # 'folderPath': String; path of folder to restore
+ # '{cloudSettings}': Check 'Cloud Projects Settings' subsection below for more information.
Project
GetMediaPool() --> MediaPool # Returns the Media Pool object.
@@ -175,6 +186,9 @@ Project
startOffsetInSamples, durationInSamples)
LoadBurnInPreset(presetName) --> Bool # Loads user defined data burn in preset for project when supplied presetName (string). Returns true if successful.
ExportCurrentFrameAsStill(filePath) --> Bool # Exports current frame as still to supplied filePath. filePath must end in valid export file format. Returns True if succssful, False otherwise.
+ GetColorGroupsList() --> [ColorGroups...] # Returns a list of all group objects in the timeline.
+ AddColorGroup(groupName) --> ColorGroup # Creates a new ColorGroup. groupName must be a unique string.
+ DeleteColorGroup(colorGroup) --> Bool # Deletes the given color group and sets clips to ungrouped.
MediaStorage
GetMountedVolumeList() --> [paths...] # Returns list of folder paths corresponding to mounted volumes displayed in Resolve’s Media Storage.
@@ -198,7 +212,7 @@ MediaPool
CreateTimelineFromClips(name, clip1, clip2,...) --> Timeline # Creates new timeline with specified name, and appends the specified MediaPoolItem objects.
CreateTimelineFromClips(name, [clips]) --> Timeline # Creates new timeline with specified name, and appends the specified MediaPoolItem objects.
CreateTimelineFromClips(name, [{clipInfo}]) --> Timeline # Creates new timeline with specified name, appending the list of clipInfos specified as a dict of "mediaPoolItem", "startFrame" (int), "endFrame" (int), "recordFrame" (int).
- ImportTimelineFromFile(filePath, {importOptions}) --> Timeline # Creates timeline based on parameters within given file (AAF/EDL/XML/FCPXML/DRT/ADL) and optional importOptions dict, with support for the keys:
+ ImportTimelineFromFile(filePath, {importOptions}) --> Timeline # Creates timeline based on parameters within given file (AAF/EDL/XML/FCPXML/DRT/ADL/OTIO) and optional importOptions dict, with support for the keys:
# "timelineName": string, specifies the name of the timeline to be created. Not valid for DRT import
# "importSourceClips": Bool, specifies whether source clips should be imported, True by default. Not valid for DRT import
# "sourceClipsPath": string, specifies a filesystem path to search for source clips if the media is inaccessible in their original path and if "importSourceClips" is True
@@ -225,6 +239,8 @@ MediaPool
ExportMetadata(fileName, [clips]) --> Bool # Exports metadata of specified clips to 'fileName' in CSV format.
# If no clips are specified, all clips from media pool will be used.
GetUniqueId() --> string # Returns a unique ID for the media pool
+ CreateStereoClip(LeftMediaPoolItem,
+ RightMediaPoolItem) --> MediaPoolItem # Takes in two existing media pool items and creates a new 3D stereoscopic media pool entry replacing the input media in the media pool.
Folder
GetClipList() --> [clips...] # Returns a list of clips (items) within the folder.
@@ -233,6 +249,8 @@ Folder
GetIsFolderStale() --> bool # Returns true if folder is stale in collaboration mode, false otherwise
GetUniqueId() --> string # Returns a unique ID for the media pool folder
Export(filePath) --> bool # Returns true if export of DRB folder to filePath is successful, false otherwise
+ TranscribeAudio() --> Bool # Transcribes audio of the MediaPoolItems within the folder and nested folders. Returns True if successful; False otherwise
+ ClearTranscription() --> Bool # Clears audio transcription of the MediaPoolItems within the folder and nested folders. Returns True if successful; False otherwise.
MediaPoolItem
GetName() --> string # Returns the clip name.
@@ -340,8 +358,12 @@ Timeline
GrabStill() --> galleryStill # Grabs still from the current video clip. Returns a GalleryStill object.
GrabAllStills(stillFrameSource) --> [galleryStill] # Grabs stills from all the clips of the timeline at 'stillFrameSource' (1 - First frame, 2 - Middle frame). Returns the list of GalleryStill objects.
GetUniqueId() --> string # Returns a unique ID for the timeline
- CreateSubtitlesFromAudio() --> Bool # Creates subtitles from audio for the timeline. Returns True on success, False otherwise.
+ CreateSubtitlesFromAudio({autoCaptionSettings}) --> Bool # Creates subtitles from audio for the timeline.
+ # Takes in optional dictionary {autoCaptionSettings}. Check 'Auto Caption Settings' subsection below for more information.
+ # Returns True on success, False otherwise.
DetectSceneCuts() --> Bool # Detects and makes scene cuts along the timeline. Returns True if successful, False otherwise.
+ ConvertTimelineToStereo() --> Bool # Converts timeline to stereo. Returns True if successful; False otherwise.
+ GetNodeGraph() --> Graph # Returns the timeline's node graph object.
TimelineItem
GetName() --> string # Returns the item name.
@@ -390,12 +412,7 @@ TimelineItem
GetStereoConvergenceValues() --> {keyframes...} # Returns a dict (offset -> value) of keyframe offsets and respective convergence values.
GetStereoLeftFloatingWindowParams() --> {keyframes...} # For the LEFT eye -> returns a dict (offset -> dict) of keyframe offsets and respective floating window params. Value at particular offset includes the left, right, top and bottom floating window values.
GetStereoRightFloatingWindowParams() --> {keyframes...} # For the RIGHT eye -> returns a dict (offset -> dict) of keyframe offsets and respective floating window params. Value at particular offset includes the left, right, top and bottom floating window values.
- GetNumNodes() --> int # Returns the number of nodes in the current graph for the timeline item
ApplyArriCdlLut() --> Bool # Applies ARRI CDL and LUT. Returns True if successful, False otherwise.
- SetLUT(nodeIndex, lutPath) --> Bool # Sets LUT on the node mapping the node index provided, 1 <= nodeIndex <= total number of nodes.
- # The lutPath can be an absolute path, or a relative path (based off custom LUT paths or the master LUT path).
- # The operation is successful for valid lut paths that Resolve has already discovered (see Project.RefreshLUTList).
- GetLUT(nodeIndex) --> String # Gets relative LUT path based on the node index provided, 1 <= nodeIndex <= total number of nodes.
SetCDL([CDL map]) --> Bool # Keys of map are: "NodeIndex", "Slope", "Offset", "Power", "Saturation", where 1 <= NodeIndex <= total number of nodes.
# Example python code - SetCDL({"NodeIndex" : "1", "Slope" : "0.5 0.4 0.2", "Offset" : "0.4 0.3 0.2", "Power" : "0.6 0.7 0.8", "Saturation" : "0.65"})
AddTake(mediaPoolItem, startFrame, endFrame) --> Bool # Adds mediaPoolItem as a new take. Initializes a take selector for the timeline item if needed. By default, the full clip extents is added. startFrame (int) and endFrame (int) are optional arguments used to specify the extents.
@@ -411,11 +428,17 @@ TimelineItem
UpdateSidecar() --> Bool # Updates sidecar file for BRAW clips or RMD file for R3D clips.
GetUniqueId() --> string # Returns a unique ID for the timeline item
LoadBurnInPreset(presetName) --> Bool # Loads user defined data burn in preset for clip when supplied presetName (string). Returns true if successful.
- GetNodeLabel(nodeIndex) --> string # Returns the label of the node at nodeIndex.
CreateMagicMask(mode) --> Bool # Returns True if magic mask was created successfully, False otherwise. mode can "F" (forward), "B" (backward), or "BI" (bidirection)
RegenerateMagicMask() --> Bool # Returns True if magic mask was regenerated successfully, False otherwise.
Stabilize() --> Bool # Returns True if stabilization was successful, False otherwise
SmartReframe() --> Bool # Performs Smart Reframe. Returns True if successful, False otherwise.
+ GetNodeGraph() --> Graph # Returns the clip's node graph object.
+ GetColorGroup() --> ColorGroup # Returns the clip's color group if one exists.
+ AssignToColorGroup(ColorGroup) --> Bool # Returns True if TiItem to successfully assigned to given ColorGroup. ColorGroup must be an existing group in the current project.
+ RemoveFromColorGroup() --> Bool # Returns True if the TiItem is successfully removed from the ColorGroup it is in.
+ ExportLUT(exportType, path) --> Bool # Exports LUTs from tiItem referring to value passed in 'exportType' (enum) for LUT size. Refer to. 'ExportLUT notes' section for possible values.
+ # Saves generated LUT in the provided 'path' (string). 'path' should include the intended file name.
+ # If an empty or incorrect extension is provided, the appropriate extension (.cube/.vlt) will be appended at the end of the path.
Gallery
GetAlbumName(galleryStillAlbum) --> string # Returns the name of the GalleryStillAlbum object 'galleryStillAlbum'.
@@ -428,17 +451,63 @@ GalleryStillAlbum
GetStills() --> [galleryStill] # Returns the list of GalleryStill objects in the album.
GetLabel(galleryStill) --> string # Returns the label of the galleryStill.
SetLabel(galleryStill, label) --> Bool # Sets the new 'label' to GalleryStill object 'galleryStill'.
- ExportStills([galleryStill], folderPath, filePrefix, format) --> Bool # Exports list of GalleryStill objects '[galleryStill]' to directory 'folderPath', with filename prefix 'filePrefix', using file format 'format' (supported formats: dpx, cin, tif, jpg, png, ppm, bmp, xpm).
+ ImportStills([filePaths]) --> Bool # Imports GalleryStill from each filePath in [filePaths] list. True if at least one still is imported successfully. False otherwise.
+ ExportStills([galleryStill], folderPath, filePrefix, format) --> Bool # Exports list of GalleryStill objects '[galleryStill]' to directory 'folderPath', with filename prefix 'filePrefix', using file format 'format' (supported formats: dpx, cin, tif, jpg, png, ppm, bmp, xpm, drx).
DeleteStills([galleryStill]) --> Bool # Deletes specified list of GalleryStill objects '[galleryStill]'.
GalleryStill # This class does not provide any API functions but the object type is used by functions in other classes.
+Graph
+ GetNumNodes() --> int # Returns the number of nodes in the graph
+ SetLUT(nodeIndex, lutPath) --> Bool # Sets LUT on the node mapping the node index provided, 1 <= nodeIndex <= self.GetNumNodes().
+ # The lutPath can be an absolute path, or a relative path (based off custom LUT paths or the master LUT path).
+ # The operation is successful for valid lut paths that Resolve has already discovered (see Project.RefreshLUTList).
+ GetLUT(nodeIndex) --> String # Gets relative LUT path based on the node index provided, 1 <= nodeIndex <= total number of nodes.
+ GetNodeLabel(nodeIndex) --> string # Returns the label of the node at nodeIndex.
+ GetToolsInNode(nodeIndex) --> [toolsList] # Returns toolsList (list of strings) of the tools used in the node indicated by given nodeIndex (int).
+
+ColorGroup
+ GetName() --> String # Returns the name (string) of the ColorGroup.
+ SetName(groupName) --> Bool # Renames ColorGroup to groupName (string).
+ GetClipsInTimeline(Timeline=CurrTimeline) --> [TimelineItem] # Returns a list of TimelineItem that are in colorGroup in the given Timeline. Timeline is Current Timeline by default.
+ GetPreClipNodeGraph() --> Graph # Returns the ColorGroup Pre-clip graph.
+ GetPostClipNodeGraph() --> Graph # Returns the ColorGroup Post-clip graph.
+
List and Dict Data Structures
-----------------------------
Beside primitive data types, Resolve's Python API mainly uses list and dict data structures. Lists are denoted by [ ... ] and dicts are denoted by { ... } above.
As Lua does not support list and dict data structures, the Lua API implements "list" as a table with indices, e.g. { [1] = listValue1, [2] = listValue2, ... }.
Similarly the Lua API implements "dict" as a table with the dictionary key as first element, e.g. { [dictKey1] = dictValue1, [dictKey2] = dictValue2, ... }.
+Keyframe Mode information
+-------------------------
+This section covers additional notes for the functions Resolve.GetKeyframeMode() and Resolve.SetKeyframeMode(keyframeMode).
+
+'keyframeMode' can be one of the following enums:
+ - resolve.KEYFRAME_MODE_ALL == 0
+ - resolve.KEYFRAME_MODE_COLOR == 1
+ - resolve.KEYFRAME_MODE_SIZING == 2
+
+Integer values returned by Resolve.GetKeyframeMode() will correspond to the enums above.
+
+Cloud Projects Settings
+--------------------------------------
+This section covers additional notes for the functions "ProjectManager:CreateCloudProject," "ProjectManager:ImportCloudProject," and "ProjectManager:RestoreCloudProject"
+
+All three functions take in a {cloudSettings} dict, that have the following keys:
+* resolve.CLOUD_SETTING_PROJECT_NAME: String, ["" by default]
+* resolve.CLOUD_SETTING_PROJECT_MEDIA_PATH: String, ["" by default]
+* resolve.CLOUD_SETTING_IS_COLLAB: Bool, [False by default]
+* resolve.CLOUD_SETTING_SYNC_MODE: syncMode (see below), [resolve.CLOUD_SYNC_PROXY_ONLY by default]
+* resolve.CLOUD_SETTING_IS_CAMERA_ACCESS: Bool [False by default]
+
+Where syncMode is one of the following values:
+* resolve.CLOUD_SYNC_NONE,
+* resolve.CLOUD_SYNC_PROXY_ONLY,
+* resolve.CLOUD_SYNC_PROXY_AND_ORIG
+
+All three "ProjectManager:CreateCloudProject," "ProjectManager:ImportCloudProject," and "ProjectManager:RestoreCloudProject" require resolve.PROJECT_MEDIA_PATH to be defined. "ProjectManager:CreateCloudProject" also requires resolve.PROJECT_NAME to be defined.
+
Looking up Project and Clip properties
--------------------------------------
This section covers additional notes for the functions "Project:GetSetting", "Project:SetSetting", "Timeline:GetSetting", "Timeline:SetSetting", "MediaPoolItem:GetClipProperty" and
@@ -478,6 +547,49 @@ Affects:
• x = MediaPoolItem:GetClipProperty('Super Scale') and MediaPoolItem:SetClipProperty('Super Scale', x)
• for '2x Enhanced' --> MediaPoolItem:SetClipProperty('Super Scale', 2, sharpnessValue, noiseReductionValue), where sharpnessValue is a float in the range [0.0, 1.0] and noiseReductionValue is a float in the range [0.0, 1.0]
+Auto Caption Settings
+----------------------
+This section covers the supported settings for the method Timeline.CreateSubtitlesFromAudio({autoCaptionSettings})
+
+The parameter setting is a dictionary containing the following keys:
+* resolve.SUBTITLE_LANGUAGE: languageID (see below), [resolve.AUTO_CAPTION_AUTO by default]
+* resolve.SUBTITLE_CAPTION_PRESET: presetType (see below), [resolve.AUTO_CAPTION_SUBTITLE_DEFAULT by default]
+* resolve.SUBTITLE_CHARS_PER_LINE: Number between 1 and 60 inclusive [42 by default]
+* resolve.SUBTITLE_LINE_BREAK: lineBreakType (see below), [resolve.AUTO_CAPTION_LINE_SINGLE by default]
+* resolve.SUBTITLE_GAP: Number between 0 and 10 inclusive [0 by default]
+
+Note that the default values for some keys may change based on values defined for other keys, as per the UI.
+For example, if the following dictionary is supplied,
+ CreateSubtitlesFromAudio( { resolve.SUBTITLE_LANGUAGE = resolve.AUTO_CAPTION_KOREAN,
+ resolve.SUBTITLE_CAPTION_PRESET = resolve.AUTO_CAPTION_NETFLIX } )
+the default value for resolve.SUBTITLE_CHARS_PER_LINE will be 16 instead of 42
+
+languageIDs:
+* resolve.AUTO_CAPTION_AUTO
+* resolve.AUTO_CAPTION_DANISH
+* resolve.AUTO_CAPTION_DUTCH
+* resolve.AUTO_CAPTION_ENGLISH
+* resolve.AUTO_CAPTION_FRENCH
+* resolve.AUTO_CAPTION_GERMAN
+* resolve.AUTO_CAPTION_ITALIAN
+* resolve.AUTO_CAPTION_JAPANESE
+* resolve.AUTO_CAPTION_KOREAN
+* resolve.AUTO_CAPTION_MANDARIN_SIMPLIFIED
+* resolve.AUTO_CAPTION_MANDARIN_TRADITIONAL
+* resolve.AUTO_CAPTION_NORWEGIAN
+* resolve.AUTO_CAPTION_PORTUGUESE
+* resolve.AUTO_CAPTION_RUSSIAN
+* resolve.AUTO_CAPTION_SPANISH
+* resolve.AUTO_CAPTION_SWEDISH
+
+presetTypes:
+* resolve.AUTO_CAPTION_SUBTITLE_DEFAULT
+* resolve.AUTO_CAPTION_TELETEXT
+* resolve.AUTO_CAPTION_NETFLIX
+
+lineBreakTypes:
+* resolve.AUTO_CAPTION_LINE_SINGLE
+* resolve.AUTO_CAPTION_LINE_DOUBLE
Looking up Render Settings
--------------------------
@@ -531,6 +643,8 @@ exportType can be one of the following constants:
- resolve.EXPORT_DOLBY_VISION_VER_4_0
- resolve.EXPORT_DOLBY_VISION_VER_5_1
- resolve.EXPORT_OTIO
+ - resolve.EXPORT_ALE
+ - resolve.EXPORT_ALE_CDL
exportSubtype can be one of the following enums:
- resolve.EXPORT_NONE
- resolve.EXPORT_AAF_NEW
@@ -627,7 +741,8 @@ The supported keys with their accepted values are:
- MOTION_EST_STANDARD_BETTER
- MOTION_EST_ENHANCED_FASTER
- MOTION_EST_ENHANCED_BETTER
- - MOTION_EST_SPEED_WRAP
+ - MOTION_EST_SPEED_WARP_BETTER
+ - MOTION_EST_SPEED_WARP_FASTER
"Scaling" : A value from the following constants
- SCALE_USE_PROJECT = 0
- SCALE_CROP
@@ -659,6 +774,16 @@ as a single argument.
Getting the values for the keys that uses constants will return the number which is in the constant
+ExportLUT notes
+---------------
+The following section covers additional notes for TimelineItem.ExportLUT(exportType, path).
+
+Supported values for 'exportType' (enum) are:
+ - resolve.EXPORT_LUT_17PTCUBE
+ - resolve.EXPORT_LUT_33PTCUBE
+ - resolve.EXPORT_LUT_65PTCUBE
+ - resolve.EXPORT_LUT_PANASONICVLUT
+
Deprecated Resolve API Functions
--------------------------------
The following API functions are deprecated.
@@ -693,7 +818,12 @@ TimelineItem
GetFusionCompNames() --> {names...} # Returns a dict of Fusion composition names associated with the timeline item.
GetFlags() --> {colors...} # Returns a dict of flag colors assigned to the item.
GetVersionNames(versionType) --> {names...} # Returns a dict of version names by provided versionType: 0 - local, 1 - remote.
-
+ GetNumNodes() --> int # Returns the number of nodes in the current graph for the timeline item
+ SetLUT(nodeIndex, lutPath) --> Bool # Sets LUT on the node mapping the node index provided, 1 <= nodeIndex <= total number of nodes.
+ # The lutPath can be an absolute path, or a relative path (based off custom LUT paths or the master LUT path).
+ # The operation is successful for valid lut paths that Resolve has already discovered (see Project.RefreshLUTList).
+ GetLUT(nodeIndex) --> String # Gets relative LUT path based on the node index provided, 1 <= nodeIndex <= total number of nodes.
+ GetNodeLabel(nodeIndex) --> string # Returns the label of the node at nodeIndex.
Unsupported Resolve API Functions
---------------------------------
diff --git a/server_addon/resolve/client/ayon_resolve/__init__.py b/server_addon/resolve/client/ayon_resolve/__init__.py
new file mode 100644
index 0000000000..ba9afb67d5
--- /dev/null
+++ b/server_addon/resolve/client/ayon_resolve/__init__.py
@@ -0,0 +1,13 @@
+from .version import __version__
+from .addon import (
+ RESOLVE_ADDON_ROOT,
+ ResolveAddon,
+)
+
+
+__all__ = (
+ "__version__",
+
+ "RESOLVE_ADDON_ROOT",
+ "ResolveAddon",
+)
diff --git a/client/ayon_core/hosts/resolve/addon.py b/server_addon/resolve/client/ayon_resolve/addon.py
similarity index 70%
rename from client/ayon_core/hosts/resolve/addon.py
rename to server_addon/resolve/client/ayon_resolve/addon.py
index 1354caabb2..706d2802b0 100644
--- a/client/ayon_core/hosts/resolve/addon.py
+++ b/server_addon/resolve/client/ayon_resolve/addon.py
@@ -2,18 +2,20 @@ import os
from ayon_core.addon import AYONAddon, IHostAddon
-from .utils import RESOLVE_ROOT_DIR
+from .version import __version__
+from .utils import RESOLVE_ADDON_ROOT
class ResolveAddon(AYONAddon, IHostAddon):
name = "resolve"
+ version = __version__
host_name = "resolve"
def get_launch_hook_paths(self, app):
if app.host_name != self.host_name:
return []
return [
- os.path.join(RESOLVE_ROOT_DIR, "hooks")
+ os.path.join(RESOLVE_ADDON_ROOT, "hooks")
]
def get_workfile_extensions(self):
diff --git a/client/ayon_core/hosts/resolve/api/__init__.py b/server_addon/resolve/client/ayon_resolve/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/__init__.py
rename to server_addon/resolve/client/ayon_resolve/api/__init__.py
diff --git a/client/ayon_core/hosts/resolve/api/action.py b/server_addon/resolve/client/ayon_resolve/api/action.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/action.py
rename to server_addon/resolve/client/ayon_resolve/api/action.py
diff --git a/client/ayon_core/hosts/resolve/api/lib.py b/server_addon/resolve/client/ayon_resolve/api/lib.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/lib.py
rename to server_addon/resolve/client/ayon_resolve/api/lib.py
diff --git a/client/ayon_core/hosts/resolve/api/menu.py b/server_addon/resolve/client/ayon_resolve/api/menu.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/menu.py
rename to server_addon/resolve/client/ayon_resolve/api/menu.py
diff --git a/client/ayon_core/hosts/resolve/api/menu_style.qss b/server_addon/resolve/client/ayon_resolve/api/menu_style.qss
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/menu_style.qss
rename to server_addon/resolve/client/ayon_resolve/api/menu_style.qss
diff --git a/client/ayon_core/hosts/resolve/api/pipeline.py b/server_addon/resolve/client/ayon_resolve/api/pipeline.py
similarity index 98%
rename from client/ayon_core/hosts/resolve/api/pipeline.py
rename to server_addon/resolve/client/ayon_resolve/api/pipeline.py
index 15e4f1203d..d6d6dc799e 100644
--- a/client/ayon_core/hosts/resolve/api/pipeline.py
+++ b/server_addon/resolve/client/ayon_resolve/api/pipeline.py
@@ -57,7 +57,7 @@ class ResolveHost(HostBase, IWorkfileHost, ILoadHost):
"""
- log.info("ayon_core.hosts.resolve installed")
+ log.info("ayon_resolve installed")
pyblish.register_host(self.name)
pyblish.register_plugin_path(PUBLISH_PATH)
@@ -246,9 +246,7 @@ def on_pyblish_instance_toggled(instance, old_value, new_value):
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
instance, old_value, new_value))
- from ayon_core.hosts.resolve.api import (
- set_publish_attribute
- )
+ from ayon_resolve.api import set_publish_attribute
# Whether instances should be passthrough based on new value
timeline_item = instance.data["item"]
diff --git a/client/ayon_core/hosts/resolve/api/plugin.py b/server_addon/resolve/client/ayon_resolve/api/plugin.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/plugin.py
rename to server_addon/resolve/client/ayon_resolve/api/plugin.py
diff --git a/client/ayon_core/hosts/resolve/api/testing_utils.py b/server_addon/resolve/client/ayon_resolve/api/testing_utils.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/testing_utils.py
rename to server_addon/resolve/client/ayon_resolve/api/testing_utils.py
diff --git a/client/ayon_core/hosts/resolve/api/todo-rendering.py b/server_addon/resolve/client/ayon_resolve/api/todo-rendering.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/todo-rendering.py
rename to server_addon/resolve/client/ayon_resolve/api/todo-rendering.py
diff --git a/client/ayon_core/hosts/resolve/api/utils.py b/server_addon/resolve/client/ayon_resolve/api/utils.py
similarity index 91%
rename from client/ayon_core/hosts/resolve/api/utils.py
rename to server_addon/resolve/client/ayon_resolve/api/utils.py
index 030534370b..d63ade9d51 100644
--- a/client/ayon_core/hosts/resolve/api/utils.py
+++ b/server_addon/resolve/client/ayon_resolve/api/utils.py
@@ -13,11 +13,11 @@ log = Logger.get_logger(__name__)
def get_resolve_module():
- from ayon_core.hosts.resolve import api
+ from ayon_resolve import api
# dont run if already loaded
if api.bmdvr:
log.info(("resolve module is assigned to "
- f"`ayon_core.hosts.resolve.api.bmdvr`: {api.bmdvr}"))
+ f"`ayon_resolve.api.bmdvr`: {api.bmdvr}"))
return api.bmdvr
try:
"""
@@ -78,6 +78,6 @@ def get_resolve_module():
api.bmdvr = bmdvr
api.bmdvf = bmdvf
log.info(("Assigning resolve module to "
- f"`ayon_core.hosts.resolve.api.bmdvr`: {api.bmdvr}"))
+ f"`ayon_resolve.api.bmdvr`: {api.bmdvr}"))
log.info(("Assigning resolve module to "
- f"`ayon_core.hosts.resolve.api.bmdvf`: {api.bmdvf}"))
+ f"`ayon_resolve.api.bmdvf`: {api.bmdvf}"))
diff --git a/client/ayon_core/hosts/resolve/api/workio.py b/server_addon/resolve/client/ayon_resolve/api/workio.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/api/workio.py
rename to server_addon/resolve/client/ayon_resolve/api/workio.py
diff --git a/client/ayon_core/hosts/resolve/hooks/pre_resolve_last_workfile.py b/server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_last_workfile.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/hooks/pre_resolve_last_workfile.py
rename to server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_last_workfile.py
diff --git a/client/ayon_core/hosts/resolve/hooks/pre_resolve_setup.py b/server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_setup.py
similarity index 99%
rename from client/ayon_core/hosts/resolve/hooks/pre_resolve_setup.py
rename to server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_setup.py
index f45e28d5ab..ffd34d7b8d 100644
--- a/client/ayon_core/hosts/resolve/hooks/pre_resolve_setup.py
+++ b/server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_setup.py
@@ -2,7 +2,7 @@ import os
from pathlib import Path
import platform
from ayon_applications import PreLaunchHook, LaunchTypes
-from ayon_core.hosts.resolve.utils import setup
+from ayon_resolve.utils import setup
class PreLaunchResolveSetup(PreLaunchHook):
diff --git a/client/ayon_core/hosts/resolve/hooks/pre_resolve_startup.py b/server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_startup.py
similarity index 77%
rename from client/ayon_core/hosts/resolve/hooks/pre_resolve_startup.py
rename to server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_startup.py
index 300564f7cc..b357b10056 100644
--- a/client/ayon_core/hosts/resolve/hooks/pre_resolve_startup.py
+++ b/server_addon/resolve/client/ayon_resolve/hooks/pre_resolve_startup.py
@@ -1,7 +1,7 @@
import os
from ayon_applications import PreLaunchHook, LaunchTypes
-import ayon_core.hosts.resolve
+from ayon_resolve import RESOLVE_ADDON_ROOT
class PreLaunchResolveStartup(PreLaunchHook):
@@ -15,8 +15,7 @@ class PreLaunchResolveStartup(PreLaunchHook):
def execute(self):
# Set the openpype prelaunch startup script path for easy access
# in the LUA .scriptlib code
- op_resolve_root = os.path.dirname(ayon_core.hosts.resolve.__file__)
- script_path = os.path.join(op_resolve_root, "startup.py")
+ script_path = os.path.join(RESOLVE_ADDON_ROOT, "startup.py")
key = "AYON_RESOLVE_STARTUP_SCRIPT"
self.launch_context.env[key] = script_path
diff --git a/client/ayon_core/hosts/nuke/vendor/google/protobuf/util/__init__.py b/server_addon/resolve/client/ayon_resolve/otio/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/nuke/vendor/google/protobuf/util/__init__.py
rename to server_addon/resolve/client/ayon_resolve/otio/__init__.py
diff --git a/client/ayon_core/hosts/resolve/otio/davinci_export.py b/server_addon/resolve/client/ayon_resolve/otio/davinci_export.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/otio/davinci_export.py
rename to server_addon/resolve/client/ayon_resolve/otio/davinci_export.py
diff --git a/client/ayon_core/hosts/resolve/otio/davinci_import.py b/server_addon/resolve/client/ayon_resolve/otio/davinci_import.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/otio/davinci_import.py
rename to server_addon/resolve/client/ayon_resolve/otio/davinci_import.py
diff --git a/client/ayon_core/hosts/resolve/otio/utils.py b/server_addon/resolve/client/ayon_resolve/otio/utils.py
similarity index 100%
rename from client/ayon_core/hosts/resolve/otio/utils.py
rename to server_addon/resolve/client/ayon_resolve/otio/utils.py
diff --git a/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py b/server_addon/resolve/client/ayon_resolve/plugins/create/create_shot_clip.py
similarity index 99%
rename from client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py
rename to server_addon/resolve/client/ayon_resolve/plugins/create/create_shot_clip.py
index cbc03da3b6..da98c8de7d 100644
--- a/client/ayon_core/hosts/resolve/plugins/create/create_shot_clip.py
+++ b/server_addon/resolve/client/ayon_resolve/plugins/create/create_shot_clip.py
@@ -1,6 +1,6 @@
# from pprint import pformat
-from ayon_core.hosts.resolve.api import plugin, lib
-from ayon_core.hosts.resolve.api.lib import (
+from ayon_resolve.api import plugin, lib
+from ayon_resolve.api.lib import (
get_video_track_names,
create_bin,
)
diff --git a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py b/server_addon/resolve/client/ayon_resolve/plugins/load/load_clip.py
similarity index 98%
rename from client/ayon_core/hosts/resolve/plugins/load/load_clip.py
rename to server_addon/resolve/client/ayon_resolve/plugins/load/load_clip.py
index 2ce1c43957..7e3a5a254e 100644
--- a/client/ayon_core/hosts/resolve/plugins/load/load_clip.py
+++ b/server_addon/resolve/client/ayon_resolve/plugins/load/load_clip.py
@@ -1,7 +1,7 @@
import ayon_api
-from ayon_core.hosts.resolve.api import lib, plugin
-from ayon_core.hosts.resolve.api.pipeline import (
+from ayon_resolve.api import lib, plugin
+from ayon_resolve.api.pipeline import (
containerise,
update_container,
)
diff --git a/server_addon/resolve/client/ayon_resolve/plugins/load/load_editorial_package.py b/server_addon/resolve/client/ayon_resolve/plugins/load/load_editorial_package.py
new file mode 100644
index 0000000000..234e7b7f71
--- /dev/null
+++ b/server_addon/resolve/client/ayon_resolve/plugins/load/load_editorial_package.py
@@ -0,0 +1,52 @@
+from pathlib import Path
+
+from ayon_core.pipeline import (
+ load,
+ get_representation_path,
+)
+
+from ayon_resolve.api import lib
+
+
+class LoadEditorialPackage(load.LoaderPlugin):
+ """Load editorial package to timeline.
+
+ Loading timeline from OTIO file included media sources
+ and timeline structure.
+ """
+
+ product_types = {"editorial_pkg"}
+
+ representations = {"*"}
+ extensions = {"otio"}
+
+ label = "Load as Timeline"
+ order = -10
+ icon = "ei.align-left"
+ color = "orange"
+
+ def load(self, context, name, namespace, data):
+ files = get_representation_path(context["representation"])
+
+ search_folder_path = Path(files).parent / "resources"
+
+ project = lib.get_current_project()
+ media_pool = project.GetMediaPool()
+
+ # create versioned bin for editorial package
+ version_name = context["version"]["name"]
+ bin_name = f"{name}_{version_name}"
+ lib.create_bin(bin_name)
+
+ import_options = {
+ "timelineName": "Editorial Package Timeline",
+ "importSourceClips": True,
+ "sourceClipsPath": search_folder_path.as_posix(),
+ }
+
+ timeline = media_pool.ImportTimelineFromFile(files, import_options)
+ print("Timeline imported: ", timeline)
+
+ def update(self, container, context):
+ # TODO: implement update method in future
+ pass
diff --git a/client/ayon_core/hosts/resolve/plugins/publish/extract_workfile.py b/server_addon/resolve/client/ayon_resolve/plugins/publish/extract_workfile.py
similarity index 95%
rename from client/ayon_core/hosts/resolve/plugins/publish/extract_workfile.py
rename to server_addon/resolve/client/ayon_resolve/plugins/publish/extract_workfile.py
index 48ebdee7e3..77d14ccdc5 100644
--- a/client/ayon_core/hosts/resolve/plugins/publish/extract_workfile.py
+++ b/server_addon/resolve/client/ayon_resolve/plugins/publish/extract_workfile.py
@@ -2,7 +2,7 @@ import os
import pyblish.api
from ayon_core.pipeline import publish
-from ayon_core.hosts.resolve.api.lib import get_project_manager
+from ayon_resolve.api.lib import get_project_manager
class ExtractWorkfile(publish.Extractor):
diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py b/server_addon/resolve/client/ayon_resolve/plugins/publish/precollect_instances.py
similarity index 99%
rename from client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py
rename to server_addon/resolve/client/ayon_resolve/plugins/publish/precollect_instances.py
index caa79c85c0..10e1eba3e3 100644
--- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_instances.py
+++ b/server_addon/resolve/client/ayon_resolve/plugins/publish/precollect_instances.py
@@ -3,7 +3,7 @@ from pprint import pformat
import pyblish
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
-from ayon_core.hosts.resolve.api.lib import (
+from ayon_resolve.api.lib import (
get_current_timeline_items,
get_timeline_item_pype_tag,
publish_clip_color,
diff --git a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py b/server_addon/resolve/client/ayon_resolve/plugins/publish/precollect_workfile.py
similarity index 94%
rename from client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py
rename to server_addon/resolve/client/ayon_resolve/plugins/publish/precollect_workfile.py
index 6158cf1d61..a388d4bc59 100644
--- a/client/ayon_core/hosts/resolve/plugins/publish/precollect_workfile.py
+++ b/server_addon/resolve/client/ayon_resolve/plugins/publish/precollect_workfile.py
@@ -3,8 +3,8 @@ from pprint import pformat
from ayon_core.pipeline import get_current_folder_path
-from ayon_core.hosts.resolve import api as rapi
-from ayon_core.hosts.resolve.otio import davinci_export
+from ayon_resolve import api as rapi
+from ayon_resolve.otio import davinci_export
class PrecollectWorkfile(pyblish.api.ContextPlugin):
diff --git a/client/ayon_core/hosts/resolve/startup.py b/server_addon/resolve/client/ayon_resolve/startup.py
similarity index 93%
rename from client/ayon_core/hosts/resolve/startup.py
rename to server_addon/resolve/client/ayon_resolve/startup.py
index 3ad0a6bf7b..7f0bd59055 100644
--- a/client/ayon_core/hosts/resolve/startup.py
+++ b/server_addon/resolve/client/ayon_resolve/startup.py
@@ -11,7 +11,7 @@ This code runs in a separate process to the main Resolve process.
"""
import os
from ayon_core.lib import Logger
-import ayon_core.hosts.resolve.api
+import ayon_resolve.api
log = Logger.get_logger(__name__)
@@ -27,7 +27,7 @@ def ensure_installed_host():
if host:
return host
- host = ayon_core.hosts.resolve.api.ResolveHost()
+ host = ayon_resolve.api.ResolveHost()
install_host(host)
return registered_host()
@@ -35,7 +35,7 @@ def ensure_installed_host():
def launch_menu():
print("Launching Resolve AYON menu..")
ensure_installed_host()
- ayon_core.hosts.resolve.api.launch_ayon_menu()
+ ayon_resolve.api.launch_ayon_menu()
def open_workfile(path):
diff --git a/client/ayon_core/hosts/resolve/utility_scripts/AYON__Menu.py b/server_addon/resolve/client/ayon_resolve/utility_scripts/AYON__Menu.py
similarity index 82%
rename from client/ayon_core/hosts/resolve/utility_scripts/AYON__Menu.py
rename to server_addon/resolve/client/ayon_resolve/utility_scripts/AYON__Menu.py
index b10b477beb..670544d605 100644
--- a/client/ayon_core/hosts/resolve/utility_scripts/AYON__Menu.py
+++ b/server_addon/resolve/client/ayon_resolve/utility_scripts/AYON__Menu.py
@@ -8,7 +8,7 @@ log = Logger.get_logger(__name__)
def main(env):
- from ayon_core.hosts.resolve.api import ResolveHost, launch_ayon_menu
+ from ayon_resolve.api import ResolveHost, launch_ayon_menu
# activate resolve from openpype
host = ResolveHost()
diff --git a/client/ayon_core/hosts/resolve/utility_scripts/ayon_startup.scriptlib b/server_addon/resolve/client/ayon_resolve/utility_scripts/ayon_startup.scriptlib
similarity index 100%
rename from client/ayon_core/hosts/resolve/utility_scripts/ayon_startup.scriptlib
rename to server_addon/resolve/client/ayon_resolve/utility_scripts/ayon_startup.scriptlib
diff --git a/client/ayon_core/hosts/resolve/utility_scripts/develop/OTIO_export.py b/server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OTIO_export.py
similarity index 96%
rename from client/ayon_core/hosts/resolve/utility_scripts/develop/OTIO_export.py
rename to server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OTIO_export.py
index c1c83eb060..4572d1354d 100644
--- a/client/ayon_core/hosts/resolve/utility_scripts/develop/OTIO_export.py
+++ b/server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OTIO_export.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
import os
-from ayon_core.hosts.resolve.otio import davinci_export as otio_export
+from ayon_resolve.otio import davinci_export as otio_export
resolve = bmd.scriptapp("Resolve") # noqa
fu = resolve.Fusion()
diff --git a/client/ayon_core/hosts/resolve/utility_scripts/develop/OTIO_import.py b/server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OTIO_import.py
similarity index 96%
rename from client/ayon_core/hosts/resolve/utility_scripts/develop/OTIO_import.py
rename to server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OTIO_import.py
index 5bbdd73402..17de1b6fc3 100644
--- a/client/ayon_core/hosts/resolve/utility_scripts/develop/OTIO_import.py
+++ b/server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OTIO_import.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
import os
-from ayon_core.hosts.resolve.otio import davinci_import as otio_import
+from ayon_resolve.otio import davinci_import as otio_import
resolve = bmd.scriptapp("Resolve") # noqa
fu = resolve.Fusion()
diff --git a/client/ayon_core/hosts/resolve/utility_scripts/develop/OpenPype_sync_util_scripts.py b/server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OpenPype_sync_util_scripts.py
similarity index 73%
rename from client/ayon_core/hosts/resolve/utility_scripts/develop/OpenPype_sync_util_scripts.py
rename to server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OpenPype_sync_util_scripts.py
index c394238860..5a069aff9e 100644
--- a/client/ayon_core/hosts/resolve/utility_scripts/develop/OpenPype_sync_util_scripts.py
+++ b/server_addon/resolve/client/ayon_resolve/utility_scripts/develop/OpenPype_sync_util_scripts.py
@@ -6,8 +6,8 @@ from ayon_core.pipeline import install_host
def main(env):
- from ayon_core.hosts.resolve.utils import setup
- import ayon_core.hosts.resolve.api as bmdvr
+ from ayon_resolve.utils import setup
+ import ayon_resolve.api as bmdvr
# Registers openpype's Global pyblish plugins
install_host(bmdvr)
setup(env)
diff --git a/client/ayon_core/hosts/resolve/utils.py b/server_addon/resolve/client/ayon_resolve/utils.py
similarity index 96%
rename from client/ayon_core/hosts/resolve/utils.py
rename to server_addon/resolve/client/ayon_resolve/utils.py
index 4ef6ea4f40..d256fda18d 100644
--- a/client/ayon_core/hosts/resolve/utils.py
+++ b/server_addon/resolve/client/ayon_resolve/utils.py
@@ -2,7 +2,7 @@ import os
import shutil
from ayon_core.lib import Logger, is_running_from_build
-RESOLVE_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
+RESOLVE_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
def setup(env):
@@ -12,7 +12,7 @@ def setup(env):
util_scripts_dir = env["RESOLVE_UTILITY_SCRIPTS_DIR"]
util_scripts_paths = [os.path.join(
- RESOLVE_ROOT_DIR,
+ RESOLVE_ADDON_ROOT,
"utility_scripts"
)]
diff --git a/server_addon/resolve/client/ayon_resolve/version.py b/server_addon/resolve/client/ayon_resolve/version.py
new file mode 100644
index 0000000000..c8f8df554c
--- /dev/null
+++ b/server_addon/resolve/client/ayon_resolve/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring AYON addon 'resolve' version."""
+__version__ = "0.2.0"
diff --git a/server_addon/resolve/package.py b/server_addon/resolve/package.py
index cf92413bce..993f700e40 100644
--- a/server_addon/resolve/package.py
+++ b/server_addon/resolve/package.py
@@ -1,3 +1,10 @@
name = "resolve"
title = "DaVinci Resolve"
-version = "0.1.0"
+version = "0.2.0"
+
+client_dir = "ayon_resolve"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/client/ayon_core/hosts/substancepainter/__init__.py b/server_addon/substancepainter/client/ayon_substancepainter/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/__init__.py
rename to server_addon/substancepainter/client/ayon_substancepainter/__init__.py
diff --git a/client/ayon_core/hosts/substancepainter/addon.py b/server_addon/substancepainter/client/ayon_substancepainter/addon.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/addon.py
rename to server_addon/substancepainter/client/ayon_substancepainter/addon.py
diff --git a/client/ayon_core/hosts/substancepainter/api/__init__.py b/server_addon/substancepainter/client/ayon_substancepainter/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/api/__init__.py
rename to server_addon/substancepainter/client/ayon_substancepainter/api/__init__.py
diff --git a/client/ayon_core/hosts/substancepainter/api/colorspace.py b/server_addon/substancepainter/client/ayon_substancepainter/api/colorspace.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/api/colorspace.py
rename to server_addon/substancepainter/client/ayon_substancepainter/api/colorspace.py
diff --git a/client/ayon_core/hosts/substancepainter/api/lib.py b/server_addon/substancepainter/client/ayon_substancepainter/api/lib.py
similarity index 90%
rename from client/ayon_core/hosts/substancepainter/api/lib.py
rename to server_addon/substancepainter/client/ayon_substancepainter/api/lib.py
index 64c39943ce..0ae3932f58 100644
--- a/client/ayon_core/hosts/substancepainter/api/lib.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/api/lib.py
@@ -3,6 +3,8 @@ import re
import json
from collections import defaultdict
+import contextlib
+import substance_painter
import substance_painter.project
import substance_painter.resource
import substance_painter.js
@@ -640,3 +642,88 @@ def prompt_new_file_with_mesh(mesh_filepath):
return
return project_mesh
+
+
+def get_filtered_export_preset(export_preset_name, channel_type_names):
+ """Return export presets included with specific channels
+ requested by users.
+
+ Args:
+ export_preset_name (str): Name of export preset
+ channel_type_list (list): A list of channel type requested by users
+
+ Returns:
+ dict: export preset data
+ """
+
+ target_maps = []
+
+ export_presets = get_export_presets()
+ export_preset_nice_name = export_presets[export_preset_name]
+ resource_presets = substance_painter.export.list_resource_export_presets()
+ preset = next(
+ (
+ preset for preset in resource_presets
+ if preset.resource_id.name == export_preset_nice_name
+ ), None
+ )
+ if preset is None:
+ return {}
+
+ maps = preset.list_output_maps()
+ for channel_map in maps:
+ for channel_name in channel_type_names:
+ if not channel_map.get("fileName"):
+ continue
+
+ if channel_name in channel_map["fileName"]:
+ target_maps.append(channel_map)
+ # Create a new preset
+ return {
+ "exportPresets": [
+ {
+ "name": export_preset_name,
+ "maps": target_maps
+ }
+ ],
+ }
+
+
+@contextlib.contextmanager
+def set_layer_stack_opacity(node_ids, channel_types):
+ """Function to set the opacity of the layer stack during
+ context
+ Args:
+ node_ids (list[int]): Substance painter root layer node ids
+ channel_types (list[str]): Channel type names as defined as
+ attributes in `substance_painter.textureset.ChannelType`
+ """
+ # Do nothing
+ if not node_ids or not channel_types:
+ yield
+ return
+
+ stack = substance_painter.textureset.get_active_stack()
+ stack_root_layers = (
+ substance_painter.layerstack.get_root_layer_nodes(stack)
+ )
+ node_ids = set(node_ids) # lookup
+ excluded_nodes = [
+ node for node in stack_root_layers
+ if node.uid() not in node_ids
+ ]
+
+ original_opacity_values = []
+ for node in excluded_nodes:
+ for channel in channel_types:
+ chan = getattr(substance_painter.textureset.ChannelType, channel)
+ original_opacity_values.append((chan, node.get_opacity(chan)))
+ try:
+ for node in excluded_nodes:
+ for channel, _ in original_opacity_values:
+ node.set_opacity(0.0, channel)
+ yield
+ finally:
+ for node in excluded_nodes:
+ for channel, opacity in original_opacity_values:
+ node.set_opacity(opacity, channel)
diff --git a/client/ayon_core/hosts/substancepainter/api/pipeline.py b/server_addon/substancepainter/client/ayon_substancepainter/api/pipeline.py
similarity index 99%
rename from client/ayon_core/hosts/substancepainter/api/pipeline.py
rename to server_addon/substancepainter/client/ayon_substancepainter/api/pipeline.py
index 23d629533c..47090c4b0a 100644
--- a/client/ayon_core/hosts/substancepainter/api/pipeline.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/api/pipeline.py
@@ -27,11 +27,11 @@ from ayon_core.lib import (
emit_event,
)
from ayon_core.pipeline.load import any_outdated_containers
-from ayon_core.hosts.substancepainter import SUBSTANCE_HOST_DIR
+from ayon_substancepainter import SUBSTANCE_HOST_DIR
from . import lib
-log = logging.getLogger("ayon_core.hosts.substance")
+log = logging.getLogger("ayon_substancepainter")
PLUGINS_DIR = os.path.join(SUBSTANCE_HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
diff --git a/client/ayon_core/hosts/substancepainter/deploy/plugins/openpype_plugin.py b/server_addon/substancepainter/client/ayon_substancepainter/deploy/plugins/ayon_plugin.py
similarity index 67%
rename from client/ayon_core/hosts/substancepainter/deploy/plugins/openpype_plugin.py
rename to server_addon/substancepainter/client/ayon_substancepainter/deploy/plugins/ayon_plugin.py
index 8ced463367..85bb56d73c 100644
--- a/client/ayon_core/hosts/substancepainter/deploy/plugins/openpype_plugin.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/deploy/plugins/ayon_plugin.py
@@ -1,6 +1,6 @@
-def cleanup_openpype_qt_widgets():
+def cleanup_ayon_qt_widgets():
"""
Workaround for Substance failing to shut down correctly
when a Qt window was still open at the time of shutting down.
@@ -8,27 +8,27 @@ def cleanup_openpype_qt_widgets():
This seems to work sometimes, but not all the time.
"""
- # TODO: Create a more reliable method to close down all OpenPype Qt widgets
+ # TODO: Create a more reliable method to close down all AYON Qt widgets
from PySide2 import QtWidgets
import substance_painter.ui
- # Kill OpenPype Qt widgets
- print("Killing OpenPype Qt widgets..")
+ # Kill AYON Qt widgets
+ print("Killing AYON Qt widgets..")
for widget in QtWidgets.QApplication.topLevelWidgets():
- if widget.__module__.startswith("openpype."):
+ if widget.__module__.startswith("ayon_"):
print(f"Deleting widget: {widget.__class__.__name__}")
substance_painter.ui.delete_ui_element(widget)
def start_plugin():
from ayon_core.pipeline import install_host
- from ayon_core.hosts.substancepainter.api import SubstanceHost
+ from ayon_substancepainter.api import SubstanceHost
install_host(SubstanceHost())
def close_plugin():
from ayon_core.pipeline import uninstall_host
- cleanup_openpype_qt_widgets()
+ cleanup_ayon_qt_widgets()
uninstall_host()
diff --git a/client/ayon_core/hosts/substancepainter/deploy/startup/openpype_load_on_first_run.py b/server_addon/substancepainter/client/ayon_substancepainter/deploy/startup/ayon_load_on_first_run.py
similarity index 78%
rename from client/ayon_core/hosts/substancepainter/deploy/startup/openpype_load_on_first_run.py
rename to server_addon/substancepainter/client/ayon_substancepainter/deploy/startup/ayon_load_on_first_run.py
index 04b610b4df..9ef119e357 100644
--- a/client/ayon_core/hosts/substancepainter/deploy/startup/openpype_load_on_first_run.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/deploy/startup/ayon_load_on_first_run.py
@@ -1,6 +1,6 @@
-"""Ease the OpenPype on-boarding process by loading the plug-in on first run"""
+"""Ease the AYON on-boarding process by loading the plug-in on first run"""
-OPENPYPE_PLUGIN_NAME = "openpype_plugin"
+AYON_PLUGIN_NAME = "ayon_plugin"
def start_plugin():
@@ -19,9 +19,9 @@ def start_plugin():
# later than this startup script, we check whether its menu initialized
is_before_plugins_menu = PLUGINS_MENU is None
- settings = get_settings(OPENPYPE_PLUGIN_NAME)
+ settings = get_settings(AYON_PLUGIN_NAME)
if settings.value(LAUNCH_AT_START_KEY, None) is None:
- print("Initializing OpenPype plug-in on first run...")
+ print("Initializing AYON plug-in on first run...")
if is_before_plugins_menu:
print("- running before 'painter_plugins_ui'")
# Delay the launch to the painter_plugins_ui initialization
@@ -29,11 +29,11 @@ def start_plugin():
else:
# Launch now
print("- running after 'painter_plugins_ui'")
- plugin_manager(OPENPYPE_PLUGIN_NAME)(True)
+ plugin_manager(AYON_PLUGIN_NAME)(True)
# Set the checked state in the menu to avoid confusion
action = next(action for action in PLUGINS_MENU._menu.actions()
- if action.text() == OPENPYPE_PLUGIN_NAME)
+ if action.text() == AYON_PLUGIN_NAME)
if action is not None:
action.blockSignals(True)
action.setChecked(True)
diff --git a/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/create/create_textures.py
similarity index 67%
rename from client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/create/create_textures.py
index f46afadb5a..8869cb5eb0 100644
--- a/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/plugins/create/create_textures.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating textures."""
-
from ayon_core.pipeline import CreatedInstance, Creator, CreatorError
from ayon_core.lib import (
EnumDef,
@@ -9,14 +8,15 @@ from ayon_core.lib import (
BoolDef
)
-from ayon_core.hosts.substancepainter.api.pipeline import (
+from ayon_substancepainter.api.pipeline import (
get_instances,
set_instance,
set_instances,
remove_instance
)
-from ayon_core.hosts.substancepainter.api.lib import get_export_presets
+from ayon_substancepainter.api.lib import get_export_presets
+import substance_painter
import substance_painter.project
@@ -28,9 +28,17 @@ class CreateTextures(Creator):
icon = "picture-o"
default_variant = "Main"
+ settings_category = "substancepainter"
+ channel_mapping = []
+
+ def apply_settings(self, project_settings):
+ settings = project_settings["substancepainter"].get("create", []) # noqa
+ if settings:
+ self.channel_mapping = settings["CreateTextures"].get(
+ "channel_mapping", [])
+
def create(self, product_name, instance_data, pre_create_data):
-
if not substance_painter.project.is_open():
raise CreatorError("Can't create a Texture Set instance without "
"an open project.")
@@ -42,11 +50,20 @@ class CreateTextures(Creator):
"exportFileFormat",
"exportSize",
"exportPadding",
- "exportDilationDistance"
+ "exportDilationDistance",
+ "useCustomExportPreset",
+ "exportChannel"
]:
if key in pre_create_data:
creator_attributes[key] = pre_create_data[key]
+ if pre_create_data.get("use_selection"):
+ stack = substance_painter.textureset.get_active_stack()
+
+ instance_data["selected_node_id"] = [
+ node_number.uid() for node_number in
+ substance_painter.layerstack.get_selected_nodes(stack)]
+
instance = self.create_instance_in_context(product_name,
instance_data)
set_instance(
@@ -88,8 +105,53 @@ class CreateTextures(Creator):
return instance
def get_instance_attr_defs(self):
+ if self.channel_mapping:
+ export_channel_enum = {
+ item["value"]: item["name"]
+ for item in self.channel_mapping
+ }
+ else:
+ export_channel_enum = {
+ "BaseColor": "Base Color",
+ "Metallic": "Metallic",
+ "Roughness": "Roughness",
+ "SpecularEdgeColor": "Specular Edge Color",
+ "Emissive": "Emissive",
+ "Opacity": "Opacity",
+ "Displacement": "Displacement",
+ "Glossiness": "Glossiness",
+ "Anisotropylevel": "Anisotropy Level",
+ "AO": "Ambient Occulsion",
+ "Anisotropyangle": "Anisotropy Angle",
+ "Transmissive": "Transmissive",
+ "Reflection": "Reflection",
+ "Diffuse": "Diffuse",
+ "Ior": "Index of Refraction",
+ "Specularlevel": "Specular Level",
+ "BlendingMask": "Blending Mask",
+ "Translucency": "Translucency",
+ "Scattering": "Scattering",
+ "ScatterColor": "Scatter Color",
+ "SheenOpacity": "Sheen Opacity",
+ "SheenRoughness": "Sheen Roughness",
+ "SheenColor": "Sheen Color",
+ "CoatOpacity": "Coat Opacity",
+ "CoatColor": "Coat Color",
+ "CoatRoughness": "Coat Roughness",
+ "CoatSpecularLevel": "Coat Specular Level",
+ "CoatNormal": "Coat Normal",
+ }
return [
+ EnumDef("exportChannel",
+ items=export_channel_enum,
+ multiselection=True,
+ default=None,
+ label="Export Channel(s)",
+ tooltip="Choose the channel which you "
+ "want to solely export. The value "
+ "is 'None' by default which exports "
+ "all channels"),
EnumDef("exportPresetUrl",
items=get_export_presets(),
label="Output Template"),
@@ -149,7 +211,6 @@ class CreateTextures(Creator):
},
default=None,
label="Size"),
-
EnumDef("exportPadding",
items={
"passthrough": "No padding (passthrough)",
@@ -172,4 +233,10 @@ class CreateTextures(Creator):
def get_pre_create_attr_defs(self):
# Use same attributes as for instance attributes
- return self.get_instance_attr_defs()
+ attr_defs = []
+ if substance_painter.application.version_info()[0] >= 10:
+ attr_defs.append(
+ BoolDef("use_selection", label="Use selection",
+ tooltip="Select Layer Stack(s) for exporting")
+ )
+ return attr_defs + self.get_instance_attr_defs()
diff --git a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/create/create_workfile.py
similarity index 97%
rename from client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/create/create_workfile.py
index 63b1c6c7da..b100e4189d 100644
--- a/client/ayon_core/hosts/substancepainter/plugins/create/create_workfile.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/plugins/create/create_workfile.py
@@ -5,7 +5,7 @@ import ayon_api
from ayon_core.pipeline import CreatedInstance, AutoCreator
-from ayon_core.hosts.substancepainter.api.pipeline import (
+from ayon_substancepainter.api.pipeline import (
set_instances,
set_instance,
get_instances
@@ -22,6 +22,7 @@ class CreateWorkfile(AutoCreator):
icon = "document"
default_variant = "Main"
+ settings_category = "substancepainter"
def create(self):
diff --git a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/load/load_mesh.py
similarity index 99%
rename from client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/load/load_mesh.py
index d5aac1191c..e2a48dd5a4 100644
--- a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/plugins/load/load_mesh.py
@@ -5,7 +5,7 @@ from ayon_core.pipeline import (
get_representation_path,
)
from ayon_core.pipeline.load import LoadError
-from ayon_core.hosts.substancepainter.api.pipeline import (
+from ayon_substancepainter.api.pipeline import (
imprint_container,
set_container_metadata,
remove_container_metadata
diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_current_file.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/collect_current_file.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/plugins/publish/collect_current_file.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/collect_current_file.py
diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/collect_textureset_images.py
similarity index 96%
rename from client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/collect_textureset_images.py
index 20aaa56993..6d2336cbc0 100644
--- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/collect_textureset_images.py
@@ -6,8 +6,9 @@ import ayon_api
import substance_painter.textureset
from ayon_core.pipeline import publish
-from ayon_core.hosts.substancepainter.api.lib import (
+from ayon_substancepainter.api.lib import (
get_parsed_export_maps,
+ get_filtered_export_preset,
strip_template
)
from ayon_core.pipeline.create import get_product_name
@@ -207,5 +208,8 @@ class CollectTextureSet(pyblish.api.InstancePlugin):
for key, value in dict(parameters).items():
if value is None:
parameters.pop(key)
-
+ channel_layer = creator_attrs.get("exportChannel", [])
+ if channel_layer:
+ maps = get_filtered_export_preset(preset_url, channel_layer)
+ config.update(maps)
return config
diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_workfile_representation.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/collect_workfile_representation.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/plugins/publish/collect_workfile_representation.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/collect_workfile_representation.py
diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/extract_textures.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/extract_textures.py
similarity index 66%
rename from client/ayon_core/hosts/substancepainter/plugins/publish/extract_textures.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/extract_textures.py
index 0fa7b52f45..52212922ae 100644
--- a/client/ayon_core/hosts/substancepainter/plugins/publish/extract_textures.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/extract_textures.py
@@ -1,6 +1,6 @@
import substance_painter.export
-
from ayon_core.pipeline import KnownPublishError, publish
+from ayon_substancepainter.api.lib import set_layer_stack_opacity
class ExtractTextures(publish.Extractor,
@@ -25,19 +25,24 @@ class ExtractTextures(publish.Extractor,
def process(self, instance):
config = instance.data["exportConfig"]
- result = substance_painter.export.export_project_textures(config)
+ creator_attrs = instance.data["creator_attributes"]
+ export_channel = creator_attrs.get("exportChannel", [])
+ node_ids = instance.data.get("selected_node_id", [])
- if result.status != substance_painter.export.ExportStatus.Success:
- raise KnownPublishError(
- "Failed to export texture set: {}".format(result.message)
- )
+ with set_layer_stack_opacity(node_ids, export_channel):
+ result = substance_painter.export.export_project_textures(config)
- # Log what files we generated
- for (texture_set_name, stack_name), maps in result.textures.items():
- # Log our texture outputs
- self.log.info(f"Exported stack: {texture_set_name} {stack_name}")
- for texture_map in maps:
- self.log.info(f"Exported texture: {texture_map}")
+ if result.status != substance_painter.export.ExportStatus.Success:
+ raise KnownPublishError(
+ "Failed to export texture set: {}".format(result.message)
+ )
+
+ # Log what files we generated
+ for (texture_set_name, stack_name), maps in result.textures.items():
+ # Log our texture outputs
+ self.log.info(f"Exported stack: {texture_set_name} {stack_name}")
+ for texture_map in maps:
+ self.log.info(f"Exported texture: {texture_map}")
# We'll insert the color space data for each image instance that we
# added into this texture set. The collector couldn't do so because
diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/increment_workfile.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/increment_workfile.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/plugins/publish/increment_workfile.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/increment_workfile.py
diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/save_workfile.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/save_workfile.py
similarity index 100%
rename from client/ayon_core/hosts/substancepainter/plugins/publish/save_workfile.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/save_workfile.py
diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/validate_ouput_maps.py b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/validate_ouput_maps.py
similarity index 71%
rename from client/ayon_core/hosts/substancepainter/plugins/publish/validate_ouput_maps.py
rename to server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/validate_ouput_maps.py
index 720771994c..3293e7f204 100644
--- a/client/ayon_core/hosts/substancepainter/plugins/publish/validate_ouput_maps.py
+++ b/server_addon/substancepainter/client/ayon_substancepainter/plugins/publish/validate_ouput_maps.py
@@ -30,11 +30,16 @@ class ValidateOutputMaps(pyblish.api.InstancePlugin):
# it will generate without actually exporting the files. So we try to
# generate the smallest size / fastest export as possible
config = copy.deepcopy(config)
+ invalid_channels = self.get_invalid_channels(instance, config)
+ if invalid_channels:
+ raise PublishValidationError(
+ "Invalid Channel(s): {} found in texture set {}".format(
+ invalid_channels, instance.name
+ ))
parameters = config["exportParameters"][0]["parameters"]
parameters["sizeLog2"] = [1, 1] # output 2x2 images (smallest)
parameters["paddingAlgorithm"] = "passthrough" # no dilation (faster)
parameters["dithering"] = False # no dithering (faster)
-
result = substance_painter.export.export_project_textures(config)
if result.status != substance_painter.export.ExportStatus.Success:
raise PublishValidationError(
@@ -108,3 +113,41 @@ class ValidateOutputMaps(pyblish.api.InstancePlugin):
message=message,
title="Missing output maps"
)
+
+ def get_invalid_channels(self, instance, config):
+ """Function to get invalid channel(s) from export channel
+ filtering
+
+ Args:
+ instance (pyblish.api.Instance): Instance
+ config (dict): export config
+
+ Raises:
+ PublishValidationError: raise Publish Validation
+ Error if any invalid channel(s) found
+
+ Returns:
+ list: invalid channel(s)
+ """
+ creator_attrs = instance.data["creator_attributes"]
+ export_channel = creator_attrs.get("exportChannel", [])
+ tmp_export_channel = copy.deepcopy(export_channel)
+ invalid_channel = []
+ if export_channel:
+ for export_preset in config.get("exportPresets", {}):
+ if not export_preset.get("maps", {}):
+ raise PublishValidationError(
+ "No Texture Map Exported with texture set: {}.".format(
+ instance.name)
+ )
+ map_names = [channel_map["fileName"] for channel_map
+ in export_preset["maps"]]
+ for channel in tmp_export_channel:
+ # Check if channel is found in at least one map
+ for map_name in map_names:
+ if channel in map_name:
+ break
+ else:
+ invalid_channel.append(channel)
+
+ return invalid_channel
diff --git a/server_addon/substancepainter/package.py b/server_addon/substancepainter/package.py
index d445b0059f..4db5ef634f 100644
--- a/server_addon/substancepainter/package.py
+++ b/server_addon/substancepainter/package.py
@@ -1,3 +1,10 @@
name = "substancepainter"
title = "Substance Painter"
-version = "0.1.1"
+version = "0.2.0"
+
+client_dir = "ayon_substancepainter"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/server_addon/substancepainter/server/settings/creator_plugins.py b/server_addon/substancepainter/server/settings/creator_plugins.py
new file mode 100644
index 0000000000..9ba7684d30
--- /dev/null
+++ b/server_addon/substancepainter/server/settings/creator_plugins.py
@@ -0,0 +1,59 @@
+from ayon_server.settings import BaseSettingsModel, SettingsField
+
+
+class ChannelMappingItemModel(BaseSettingsModel):
+ _layout = "compact"
+ name: str = SettingsField(title="Channel Type")
+ value: str = SettingsField(title="Channel Map")
+
+
+class CreateTextureModel(BaseSettingsModel):
+ channel_mapping: list[ChannelMappingItemModel] = SettingsField(
+ default_factory=list, title="Channel Mapping")
+
+
+class CreatorsModel(BaseSettingsModel):
+ CreateTextures: CreateTextureModel = SettingsField(
+ default_factory=CreateTextureModel,
+ title="Create Textures"
+ )
+
+
+DEFAULT_CREATOR_SETTINGS = {
+ "CreateTextures": {
+ "channel_mapping": [
+ {"name": "Base Color", "value": "BaseColor"},
+ {"name": "Metallic", "value": "Metallic"},
+ {"name": "Roughness", "value": "Roughness"},
+ {"name": "Normal", "value": "Normal"},
+ {"name": "Height", "value": "Height"},
+ {"name": "Specular Edge Color",
+ "value": "SpecularEdgeColor"},
+ {"name": "Opacity", "value": "Opacity"},
+ {"name": "Displacement", "value": "Displacement"},
+ {"name": "Glossiness", "value": "Glossiness"},
+ {"name": "Anisotropy Level",
+ "value": "Anisotropylevel"},
+ {"name": "Ambient Occulsion", "value": "AO"},
+ {"name": "Anisotropy Angle",
+ "value": "Anisotropyangle"},
+ {"name": "Transmissive", "value": "Transmissive"},
+ {"name": "Reflection", "value": "Reflection"},
+ {"name": "Diffuse", "value": "Diffuse"},
+ {"name": "Index of Refraction", "value": "Ior"},
+ {"name": "Specular Level", "value": "Specularlevel"},
+ {"name": "Blending Mask", "value": "BlendingMask"},
+ {"name": "Translucency", "value": "Translucency"},
+ {"name": "Scattering", "value": "Scattering"},
+ {"name": "Scatter Color", "value": "ScatterColor"},
+ {"name": "Sheen Opacity", "value": "SheenOpacity"},
+ {"name": "Sheen Color", "value": "SheenColor"},
+ {"name": "Coat Opacity", "value": "CoatOpacity"},
+ {"name": "Coat Color", "value": "CoatColor"},
+ {"name": "Coat Roughness", "value": "CoatRoughness"},
+ {"name": "CoatSpecularLevel",
+ "value": "Coat Specular Level"},
+ {"name": "CoatNormal", "value": "Coat Normal"}
+ ],
+ }
+}
\ No newline at end of file
diff --git a/server_addon/substancepainter/server/settings/main.py b/server_addon/substancepainter/server/settings/main.py
index 93523fd650..9a13d2c32f 100644
--- a/server_addon/substancepainter/server/settings/main.py
+++ b/server_addon/substancepainter/server/settings/main.py
@@ -1,5 +1,6 @@
from ayon_server.settings import BaseSettingsModel, SettingsField
from .imageio import ImageIOSettings, DEFAULT_IMAGEIO_SETTINGS
+from .creator_plugins import CreatorsModel, DEFAULT_CREATOR_SETTINGS
from .load_plugins import LoadersModel, DEFAULT_LOADER_SETTINGS
@@ -18,6 +19,8 @@ class SubstancePainterSettings(BaseSettingsModel):
default_factory=list,
title="Shelves"
)
+ create: CreatorsModel = SettingsField(
+ default_factory=DEFAULT_CREATOR_SETTINGS, title="Creators")
load: LoadersModel = SettingsField(
default_factory=DEFAULT_LOADER_SETTINGS, title="Loaders")
@@ -25,5 +28,7 @@ class SubstancePainterSettings(BaseSettingsModel):
DEFAULT_SPAINTER_SETTINGS = {
"imageio": DEFAULT_IMAGEIO_SETTINGS,
"shelves": [],
+ "create": DEFAULT_CREATOR_SETTINGS,
"load": DEFAULT_LOADER_SETTINGS,
+
}
diff --git a/client/ayon_core/hosts/traypublisher/__init__.py b/server_addon/traypublisher/client/ayon_traypublisher/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/__init__.py
rename to server_addon/traypublisher/client/ayon_traypublisher/__init__.py
diff --git a/client/ayon_core/hosts/traypublisher/addon.py b/server_addon/traypublisher/client/ayon_traypublisher/addon.py
similarity index 93%
rename from client/ayon_core/hosts/traypublisher/addon.py
rename to server_addon/traypublisher/client/ayon_traypublisher/addon.py
index 3dd275f223..5432cb1a92 100644
--- a/client/ayon_core/hosts/traypublisher/addon.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/addon.py
@@ -29,8 +29,8 @@ class TrayPublishAddon(AYONAddon, IHostAddon, ITrayAction):
def on_action_trigger(self):
self.run_traypublisher()
- def connect_with_addons(self, enabled_modules):
- """Collect publish paths from other modules."""
+ def connect_with_addons(self, enabled_addons):
+ """Collect publish paths from other addons."""
publish_paths = self.manager.collect_plugin_paths()["publish"]
self.publish_paths.extend(publish_paths)
@@ -55,9 +55,9 @@ def cli_main():
def launch():
"""Launch TrayPublish tool UI."""
- from ayon_core.tools import traypublisher
+ from ayon_traypublisher import ui
- traypublisher.main()
+ ui.main()
@cli_main.command()
diff --git a/client/ayon_core/hosts/traypublisher/api/__init__.py b/server_addon/traypublisher/client/ayon_traypublisher/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/api/__init__.py
rename to server_addon/traypublisher/client/ayon_traypublisher/api/__init__.py
diff --git a/client/ayon_core/hosts/traypublisher/api/editorial.py b/server_addon/traypublisher/client/ayon_traypublisher/api/editorial.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/api/editorial.py
rename to server_addon/traypublisher/client/ayon_traypublisher/api/editorial.py
diff --git a/client/ayon_core/hosts/traypublisher/api/pipeline.py b/server_addon/traypublisher/client/ayon_traypublisher/api/pipeline.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/api/pipeline.py
rename to server_addon/traypublisher/client/ayon_traypublisher/api/pipeline.py
diff --git a/client/ayon_core/hosts/traypublisher/api/plugin.py b/server_addon/traypublisher/client/ayon_traypublisher/api/plugin.py
similarity index 99%
rename from client/ayon_core/hosts/traypublisher/api/plugin.py
rename to server_addon/traypublisher/client/ayon_traypublisher/api/plugin.py
index 257d01eb50..973eb65b11 100644
--- a/client/ayon_core/hosts/traypublisher/api/plugin.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/api/plugin.py
@@ -22,7 +22,7 @@ from .pipeline import (
)
REVIEW_EXTENSIONS = set(IMAGE_EXTENSIONS) | set(VIDEO_EXTENSIONS)
-SHARED_DATA_KEY = "openpype.traypublisher.instances"
+SHARED_DATA_KEY = "ayon.traypublisher.instances"
class HiddenTrayPublishCreator(HiddenCreator):
diff --git a/client/ayon_core/hosts/traypublisher/batch_parsing.py b/server_addon/traypublisher/client/ayon_traypublisher/batch_parsing.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/batch_parsing.py
rename to server_addon/traypublisher/client/ayon_traypublisher/batch_parsing.py
diff --git a/client/ayon_core/hosts/traypublisher/csv_publish.py b/server_addon/traypublisher/client/ayon_traypublisher/csv_publish.py
similarity index 96%
rename from client/ayon_core/hosts/traypublisher/csv_publish.py
rename to server_addon/traypublisher/client/ayon_traypublisher/csv_publish.py
index b43792a357..b7906c5706 100644
--- a/client/ayon_core/hosts/traypublisher/csv_publish.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/csv_publish.py
@@ -1,5 +1,3 @@
-import os
-
import pyblish.api
import pyblish.util
@@ -8,7 +6,7 @@ from ayon_core.lib.attribute_definitions import FileDefItem
from ayon_core.pipeline import install_host
from ayon_core.pipeline.create import CreateContext
-from ayon_core.hosts.traypublisher.api import TrayPublisherHost
+from ayon_traypublisher.api import TrayPublisherHost
def csvpublish(
diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_colorspace_look.py
similarity index 93%
rename from client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_colorspace_look.py
index 4d865c1c5c..901bd758ba 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/create/create_colorspace_look.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_colorspace_look.py
@@ -15,13 +15,13 @@ from ayon_core.pipeline import (
CreatorError
)
from ayon_core.pipeline import colorspace
-from ayon_core.hosts.traypublisher.api.plugin import TrayPublishCreator
+from ayon_traypublisher.api.plugin import TrayPublishCreator
class CreateColorspaceLook(TrayPublishCreator):
"""Creates colorspace look files."""
- identifier = "io.openpype.creators.traypublisher.colorspace_look"
+ identifier = "io.ayon.creators.traypublisher.colorspace_look"
label = "Colorspace Look"
product_type = "ociolook"
description = "Publishes color space look file."
@@ -156,14 +156,9 @@ This creator publishes color space look file (LUT).
]
def apply_settings(self, project_settings):
- host = self.create_context.host
- host_name = host.name
- project_name = host.get_current_project_name()
- config_data = colorspace.get_imageio_config(
- project_name, host_name,
+ config_data = colorspace.get_current_context_imageio_config_preset(
project_settings=project_settings
)
-
if not config_data:
self.enabled = False
return
diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_csv_ingest.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_csv_ingest.py
similarity index 99%
rename from client/ayon_core/hosts/traypublisher/plugins/create/create_csv_ingest.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_csv_ingest.py
index 8143e8b45b..5a5deeada8 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/create/create_csv_ingest.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_csv_ingest.py
@@ -13,9 +13,7 @@ from ayon_core.lib.transcoding import (
VIDEO_EXTENSIONS, IMAGE_EXTENSIONS
)
from ayon_core.pipeline.create import CreatorError
-from ayon_core.hosts.traypublisher.api.plugin import (
- TrayPublishCreator
-)
+from ayon_traypublisher.api.plugin import TrayPublishCreator
class IngestCSV(TrayPublishCreator):
diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_editorial.py
similarity index 99%
rename from client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_editorial.py
index 4057aee9a6..a2f6f211f5 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/create/create_editorial.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_editorial.py
@@ -4,11 +4,11 @@ from copy import deepcopy
import ayon_api
import opentimelineio as otio
-from ayon_core.hosts.traypublisher.api.plugin import (
+from ayon_traypublisher.api.plugin import (
TrayPublishCreator,
HiddenTrayPublishCreator
)
-from ayon_core.hosts.traypublisher.api.editorial import (
+from ayon_traypublisher.api.editorial import (
ShotMetadataSolver
)
from ayon_core.pipeline import CreatedInstance
diff --git a/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_editorial_package.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_editorial_package.py
new file mode 100644
index 0000000000..bc003c0601
--- /dev/null
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_editorial_package.py
@@ -0,0 +1,96 @@
+from pathlib import Path
+
+from ayon_core.pipeline import (
+ CreatedInstance,
+)
+
+from ayon_core.lib.attribute_definitions import (
+ FileDef,
+ BoolDef,
+ TextDef,
+)
+from ayon_traypublisher.api.plugin import TrayPublishCreator
+
+
+class EditorialPackageCreator(TrayPublishCreator):
+ """Creates instance for OTIO file from published folder.
+
+ Folder contains OTIO file and exported .mov files. Process should publish
+ whole folder as single `editorial_pkg` product type and (possibly) convert
+ .mov files into different format and copy them into `publish` `resources`
+ subfolder.
+ """
+ identifier = "editorial_pkg"
+ label = "Editorial package"
+ product_type = "editorial_pkg"
+ description = "Publish folder with OTIO file and resources"
+
+ # Position batch creator after simple creators
+ order = 120
+
+ conversion_enabled = False
+
+ def apply_settings(self, project_settings):
+ self.conversion_enabled = (
+ project_settings["traypublisher"]
+ ["publish"]
+ ["ExtractEditorialPckgConversion"]
+ ["conversion_enabled"]
+ )
+
+ def get_icon(self):
+ return "fa.folder"
+
+ def create(self, product_name, instance_data, pre_create_data):
+ folder_path = pre_create_data.get("folder_path")
+ if not folder_path:
+ return
+
+ instance_data["creator_attributes"] = {
+ "folder_path": (Path(folder_path["directory"]) /
+ Path(folder_path["filenames"][0])).as_posix(),
+ "conversion_enabled": pre_create_data["conversion_enabled"]
+ }
+
+ # Create new instance
+ new_instance = CreatedInstance(self.product_type, product_name,
+ instance_data, self)
+ self._store_new_instance(new_instance)
+
+ def get_pre_create_attr_defs(self):
+ # Use same attributes as for instance attributes
+ return [
+ FileDef(
+ "folder_path",
+ folders=True,
+ single_item=True,
+ extensions=[],
+ allow_sequences=False,
+ label="Folder path"
+ ),
+ BoolDef("conversion_enabled",
+ tooltip="Convert to output defined in Settings.",
+ default=self.conversion_enabled,
+ label="Convert resources"),
+ ]
+
+ def get_instance_attr_defs(self):
+ return [
+ TextDef(
+ "folder_path",
+ label="Folder path",
+ disabled=True
+ ),
+ BoolDef("conversion_enabled",
+ tooltip="Convert to output defined in Settings.",
+ label="Convert resources"),
+ ]
+
+ def get_detail_description(self):
+ return """# Publish folder with OTIO file and video clips
+
+ Folder contains OTIO file and exported .mov files. Process should
+ publish whole folder as single `editorial_pkg` product type and
+ (possibly) convert .mov files into different format and copy them into
+ `publish` `resources` subfolder.
+ """
diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_from_settings.py
similarity index 88%
rename from client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_from_settings.py
index fe7ba4c4a4..13cf92ab10 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/create/create_from_settings.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_from_settings.py
@@ -6,7 +6,7 @@ log = Logger.get_logger(__name__)
def initialize():
- from ayon_core.hosts.traypublisher.api.plugin import SettingsCreator
+ from ayon_traypublisher.api.plugin import SettingsCreator
project_name = os.environ["AYON_PROJECT_NAME"]
project_settings = get_project_settings(project_name)
diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_movie_batch.py
similarity index 97%
rename from client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_movie_batch.py
index 546408b4d6..77b9b0df0a 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/create/create_movie_batch.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_movie_batch.py
@@ -17,8 +17,8 @@ from ayon_core.pipeline.create import (
TaskNotSetError,
)
-from ayon_core.hosts.traypublisher.api.plugin import TrayPublishCreator
-from ayon_core.hosts.traypublisher.batch_parsing import (
+from ayon_traypublisher.api.plugin import TrayPublishCreator
+from ayon_traypublisher.batch_parsing import (
get_folder_entity_from_filename
)
diff --git a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_online.py
similarity index 96%
rename from client/ayon_core/hosts/traypublisher/plugins/create/create_online.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_online.py
index f48037701e..135a11c0c6 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/create/create_online.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/create/create_online.py
@@ -14,13 +14,13 @@ from ayon_core.pipeline import (
CreatedInstance,
CreatorError
)
-from ayon_core.hosts.traypublisher.api.plugin import TrayPublishCreator
+from ayon_traypublisher.api.plugin import TrayPublishCreator
class OnlineCreator(TrayPublishCreator):
"""Creates instance from file and retains its original name."""
- identifier = "io.openpype.creators.traypublisher.online"
+ identifier = "io.ayon.creators.traypublisher.online"
label = "Online"
product_type = "online"
description = "Publish file retaining its original file name"
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_app_name.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_app_name.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_app_name.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_app_name.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_clip_instances.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_clip_instances.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_clip_instances.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_clip_instances.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_colorspace_look.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_colorspace_look.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_colorspace_look.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_colorspace_look.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_csv_ingest_instance_data.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_csv_ingest_instance_data.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_csv_ingest_instance_data.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_csv_ingest_instance_data.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_instances.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_editorial_instances.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_instances.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_editorial_instances.py
diff --git a/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_editorial_package.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_editorial_package.py
new file mode 100644
index 0000000000..fb7d5cd5a1
--- /dev/null
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_editorial_package.py
@@ -0,0 +1,58 @@
+"""Produces instance.data["editorial_pkg"] data used during integration.
+
+Requires:
+ instance.data["creator_attributes"]["path"] - from creator
+
+Provides:
+ instance -> editorial_pkg (dict):
+ folder_path (str)
+ otio_path (str) - from dragged folder
+ resource_paths (list)
+
+"""
+import os
+
+import pyblish.api
+
+from ayon_core.lib.transcoding import VIDEO_EXTENSIONS
+
+
+class CollectEditorialPackage(pyblish.api.InstancePlugin):
+ """Collects path to OTIO file and resources"""
+
+ label = "Collect Editorial Package"
+ order = pyblish.api.CollectorOrder - 0.1
+
+ hosts = ["traypublisher"]
+ families = ["editorial_pkg"]
+
+ def process(self, instance):
+ folder_path = instance.data["creator_attributes"]["folder_path"]
+ if not folder_path or not os.path.exists(folder_path):
+ self.log.info((
+ "Instance doesn't contain collected existing folder path."
+ ))
+ return
+
+ instance.data["editorial_pkg"] = {}
+ instance.data["editorial_pkg"]["folder_path"] = folder_path
+
+ otio_path, resource_paths = (
+ self._get_otio_and_resource_paths(folder_path))
+
+ instance.data["editorial_pkg"]["otio_path"] = otio_path
+ instance.data["editorial_pkg"]["resource_paths"] = resource_paths
+
+ def _get_otio_and_resource_paths(self, folder_path):
+ otio_path = None
+ resource_paths = []
+
+ file_names = os.listdir(folder_path)
+ for filename in file_names:
+ _, ext = os.path.splitext(filename)
+ file_path = os.path.join(folder_path, filename)
+ if ext == ".otio":
+ otio_path = file_path
+ elif ext in VIDEO_EXTENSIONS:
+ resource_paths.append(file_path)
+ return otio_path, resource_paths
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_reviewable.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_editorial_reviewable.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_editorial_reviewable.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_editorial_reviewable.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_explicit_colorspace.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_explicit_colorspace.py
similarity index 85%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_explicit_colorspace.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_explicit_colorspace.py
index 8e29a0048d..5fbb9a6f4c 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_explicit_colorspace.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_explicit_colorspace.py
@@ -1,10 +1,7 @@
import pyblish.api
-from ayon_core.pipeline import (
- publish,
- registered_host
-)
from ayon_core.lib import EnumDef
from ayon_core.pipeline import colorspace
+from ayon_core.pipeline import publish
from ayon_core.pipeline.publish import KnownPublishError
@@ -19,9 +16,10 @@ class CollectColorspace(pyblish.api.InstancePlugin,
families = ["render", "plate", "reference", "image", "online"]
enabled = False
- colorspace_items = [
+ default_colorspace_items = [
(None, "Don't override")
]
+ colorspace_items = list(default_colorspace_items)
colorspace_attr_show = False
config_items = None
@@ -69,14 +67,13 @@ class CollectColorspace(pyblish.api.InstancePlugin,
@classmethod
def apply_settings(cls, project_settings):
- host = registered_host()
- host_name = host.name
- project_name = host.get_current_project_name()
- config_data = colorspace.get_imageio_config(
- project_name, host_name,
+ config_data = colorspace.get_current_context_imageio_config_preset(
project_settings=project_settings
)
+ enabled = False
+ colorspace_items = list(cls.default_colorspace_items)
+ config_items = None
if config_data:
filepath = config_data["path"]
config_items = colorspace.get_ocio_config_colorspaces(filepath)
@@ -85,9 +82,11 @@ class CollectColorspace(pyblish.api.InstancePlugin,
include_aliases=True,
include_roles=True
)
- cls.config_items = config_items
- cls.colorspace_items.extend(labeled_colorspaces)
- cls.enabled = True
+ colorspace_items.extend(labeled_colorspaces)
+
+ cls.config_items = config_items
+ cls.colorspace_items = colorspace_items
+ cls.enabled = enabled
@classmethod
def get_attribute_defs(cls):
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_frame_data_from_folder_entity.py
similarity index 64%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_frame_data_from_folder_entity.py
index 4d203649c7..2e564a2e4e 100644
--- a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_frame_data_from_asset_entity.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_frame_data_from_folder_entity.py
@@ -10,9 +10,13 @@ class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.491
label = "Collect Missing Frame Data From Folder"
- families = ["plate", "pointcache",
- "vdbcache", "online",
- "render"]
+ families = [
+ "plate",
+ "pointcache",
+ "vdbcache",
+ "online",
+ "render",
+ ]
hosts = ["traypublisher"]
def process(self, instance):
@@ -22,16 +26,26 @@ class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin):
"frameStart",
"frameEnd",
"handleStart",
- "handleEnd"
+ "handleEnd",
):
if key not in instance.data:
missing_keys.append(key)
+
+ # Skip the logic if all keys are already collected.
+ # NOTE: In editorial is not 'folderEntity' filled, so it would crash
+ # even if we don't need it.
+ if not missing_keys:
+ return
+
keys_set = []
folder_attributes = instance.data["folderEntity"]["attrib"]
for key in missing_keys:
if key in folder_attributes:
instance.data[key] = folder_attributes[key]
keys_set.append(key)
+
if keys_set:
- self.log.debug(f"Frame range data {keys_set} "
- "has been collected from folder entity.")
+ self.log.debug(
+ f"Frame range data {keys_set} "
+ "has been collected from folder entity."
+ )
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_movie_batch.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_movie_batch.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_movie_batch.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_movie_batch.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_online_file.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_online_file.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_online_file.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_online_file.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_review_frames.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_review_frames.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_review_frames.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_review_frames.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_sequence_frame_data.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_sequence_frame_data.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_sequence_frame_data.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_sequence_frame_data.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_shot_instances.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_shot_instances.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_shot_instances.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_simple_instances.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_simple_instances.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_simple_instances.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_simple_instances.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/collect_source.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_source.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/collect_source.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/collect_source.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_colorspace_look.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_colorspace_look.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/extract_colorspace_look.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_colorspace_look.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/extract_csv_file.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_csv_file.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/extract_csv_file.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_csv_file.py
diff --git a/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_editorial_pckg.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_editorial_pckg.py
new file mode 100644
index 0000000000..3e391b5f6e
--- /dev/null
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_editorial_pckg.py
@@ -0,0 +1,232 @@
+import copy
+import os.path
+import subprocess
+
+import opentimelineio
+
+import pyblish.api
+
+from ayon_core.lib import get_ffmpeg_tool_args, run_subprocess
+from ayon_core.pipeline import publish
+
+
+class ExtractEditorialPckgConversion(publish.Extractor):
+ """Replaces movie paths in otio file with publish rootless
+
+ Prepares movie resources for integration (adds them to `transfers`).
+ Converts .mov files according to output definition.
+ """
+
+ label = "Extract Editorial Package"
+ order = pyblish.api.ExtractorOrder - 0.45
+ hosts = ["traypublisher"]
+ families = ["editorial_pkg"]
+
+ def process(self, instance):
+ editorial_pkg_data = instance.data.get("editorial_pkg")
+
+ otio_path = editorial_pkg_data["otio_path"]
+ otio_basename = os.path.basename(otio_path)
+ staging_dir = self.staging_dir(instance)
+
+ editorial_pkg_repre = {
+ 'name': "editorial_pkg",
+ 'ext': "otio",
+ 'files': otio_basename,
+ "stagingDir": staging_dir,
+ }
+ otio_staging_path = os.path.join(staging_dir, otio_basename)
+
+ instance.data["representations"].append(editorial_pkg_repre)
+
+ publish_resource_folder = self._get_publish_resource_folder(instance)
+ resource_paths = editorial_pkg_data["resource_paths"]
+ transfers = self._get_transfers(resource_paths,
+ publish_resource_folder)
+
+ project_settings = instance.context.data["project_settings"]
+ output_def = (project_settings["traypublisher"]
+ ["publish"]
+ ["ExtractEditorialPckgConversion"]
+ ["output"])
+
+ conversion_enabled = (instance.data["creator_attributes"]
+ ["conversion_enabled"])
+
+ if conversion_enabled and output_def["ext"]:
+ transfers = self._convert_resources(output_def, transfers)
+
+ instance.data["transfers"] = transfers
+
+ source_to_rootless = self._get_resource_path_mapping(instance,
+ transfers)
+
+ otio_data = editorial_pkg_data["otio_data"]
+ otio_data = self._replace_target_urls(otio_data, source_to_rootless)
+
+ opentimelineio.adapters.write_to_file(otio_data, otio_staging_path)
+
+ self.log.info("Added Editorial Package representation: {}".format(
+ editorial_pkg_repre))
+
+ def _get_publish_resource_folder(self, instance):
+ """Calculates publish folder and create it."""
+ publish_path = self._get_published_path(instance)
+ publish_folder = os.path.dirname(publish_path)
+ publish_resource_folder = os.path.join(publish_folder, "resources")
+
+ if not os.path.exists(publish_resource_folder):
+ os.makedirs(publish_resource_folder, exist_ok=True)
+ return publish_resource_folder
+
+ def _get_resource_path_mapping(self, instance, transfers):
+ """Returns dict of {source_mov_path: rootless_published_path}."""
+ replace_paths = {}
+ anatomy = instance.context.data["anatomy"]
+ for source, destination in transfers:
+ rootless_path = self._get_rootless(anatomy, destination)
+ source_file_name = os.path.basename(source)
+ replace_paths[source_file_name] = rootless_path
+ return replace_paths
+
+ def _get_transfers(self, resource_paths, publish_resource_folder):
+ """Returns list of tuples (source, destination) with movie paths."""
+ transfers = []
+ for res_path in resource_paths:
+ res_basename = os.path.basename(res_path)
+ pub_res_path = os.path.join(publish_resource_folder, res_basename)
+ transfers.append((res_path, pub_res_path))
+ return transfers
+
+ def _replace_target_urls(self, otio_data, replace_paths):
+ """Replace original movie paths with published rootless ones."""
+ for track in otio_data.tracks:
+ for clip in track:
+ # Check if the clip has a media reference
+ if clip.media_reference is not None:
+ # Access the target_url from the media reference
+ target_url = clip.media_reference.target_url
+ if not target_url:
+ continue
+ file_name = os.path.basename(target_url)
+ replace_path = replace_paths.get(file_name)
+ if replace_path:
+ clip.media_reference.target_url = replace_path
+ if clip.name == file_name:
+ clip.name = os.path.basename(replace_path)
+
+ return otio_data
+
+ def _get_rootless(self, anatomy, path):
+ """Try to find rootless {root[work]} path from `path`"""
+ success, rootless_path = anatomy.find_root_template_from_path(
+ path)
+ if not success:
+ # `rootless_path` is not set to `output_dir` if none of roots match
+ self.log.warning(
+ f"Could not find root path for remapping '{path}'."
+ )
+ rootless_path = path
+
+ return rootless_path
+
+ def _get_published_path(self, instance):
+ """Calculates expected `publish` folder"""
+ # determine published path from Anatomy.
+ template_data = instance.data.get("anatomyData")
+ rep = instance.data["representations"][0]
+ template_data["representation"] = rep.get("name")
+ template_data["ext"] = rep.get("ext")
+ template_data["comment"] = None
+
+ anatomy = instance.context.data["anatomy"]
+ template_data["root"] = anatomy.roots
+ template = anatomy.get_template_item("publish", "default", "path")
+ template_filled = template.format_strict(template_data)
+ return os.path.normpath(template_filled)
+
+ def _convert_resources(self, output_def, transfers):
+ """Converts all resource files to configured format."""
+ out_extension = output_def["ext"]
+ if not out_extension:
+ self.log.warning("No output extension configured in "
+ "ayon+settings://traypublisher/publish/ExtractEditorialPckgConversion") # noqa
+ return transfers
+
+ final_transfers = []
+ out_def_ffmpeg_args = output_def["ffmpeg_args"]
+ ffmpeg_input_args = [
+ value.strip()
+ for value in out_def_ffmpeg_args["input"]
+ if value.strip()
+ ]
+ ffmpeg_video_filters = [
+ value.strip()
+ for value in out_def_ffmpeg_args["video_filters"]
+ if value.strip()
+ ]
+ ffmpeg_audio_filters = [
+ value.strip()
+ for value in out_def_ffmpeg_args["audio_filters"]
+ if value.strip()
+ ]
+ ffmpeg_output_args = [
+ value.strip()
+ for value in out_def_ffmpeg_args["output"]
+ if value.strip()
+ ]
+ ffmpeg_input_args = self._split_ffmpeg_args(ffmpeg_input_args)
+
+ generic_args = [
+ subprocess.list2cmdline(get_ffmpeg_tool_args("ffmpeg"))
+ ]
+ generic_args.extend(ffmpeg_input_args)
+ if ffmpeg_video_filters:
+ generic_args.append("-filter:v")
+ generic_args.append(
+ "\"{}\"".format(",".join(ffmpeg_video_filters)))
+
+ if ffmpeg_audio_filters:
+ generic_args.append("-filter:a")
+ generic_args.append(
+ "\"{}\"".format(",".join(ffmpeg_audio_filters)))
+
+ for source, destination in transfers:
+ base_name = os.path.basename(destination)
+ file_name, ext = os.path.splitext(base_name)
+ dest_path = os.path.join(os.path.dirname(destination),
+ f"{file_name}.{out_extension}")
+ final_transfers.append((source, dest_path))
+
+ all_args = copy.deepcopy(generic_args)
+ all_args.append(f"-i \"{source}\"")
+ all_args.extend(ffmpeg_output_args) # order matters
+ all_args.append(f"\"{dest_path}\"")
+ subprcs_cmd = " ".join(all_args)
+
+ # run subprocess
+ self.log.debug("Executing: {}".format(subprcs_cmd))
+ run_subprocess(subprcs_cmd, shell=True, logger=self.log)
+ return final_transfers
+
+ def _split_ffmpeg_args(self, in_args):
+ """Makes sure all entered arguments are separated in individual items.
+
+ Split each argument string with " -" to identify if string contains
+ one or more arguments.
+ """
+ splitted_args = []
+ for arg in in_args:
+ sub_args = arg.split(" -")
+ if len(sub_args) == 1:
+ if arg and arg not in splitted_args:
+ splitted_args.append(arg)
+ continue
+
+ for idx, arg in enumerate(sub_args):
+ if idx != 0:
+ arg = "-" + arg
+
+ if arg and arg not in splitted_args:
+ splitted_args.append(arg)
+ return splitted_args
diff --git a/client/ayon_core/plugins/publish/extract_trim_video_audio.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_trim_video_audio.py
similarity index 100%
rename from client/ayon_core/plugins/publish/extract_trim_video_audio.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/extract_trim_video_audio.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/help/validate_existing_version.xml
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_existing_version.xml
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/help/validate_existing_version.xml
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_frame_ranges.xml b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/help/validate_frame_ranges.xml
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/help/validate_frame_ranges.xml
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/help/validate_frame_ranges.xml
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_colorspace.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_colorspace.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/validate_colorspace.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_colorspace.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_colorspace_look.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_colorspace_look.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/validate_colorspace_look.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_colorspace_look.py
diff --git a/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_editorial_package.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_editorial_package.py
new file mode 100644
index 0000000000..02793516e2
--- /dev/null
+++ b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_editorial_package.py
@@ -0,0 +1,79 @@
+import os
+import opentimelineio
+from opentimelineio.exceptions import UnsupportedSchemaError
+
+
+import pyblish.api
+from ayon_core.pipeline import PublishValidationError
+
+
+class ValidateEditorialPackage(pyblish.api.InstancePlugin):
+ """Checks that published folder contains all resources from otio
+
+ Currently checks only by file names and expects flat structure.
+ It ignores path to resources in otio file as folder might be dragged in and
+ published from different location than it was created.
+ """
+
+ label = "Validate Editorial Package"
+ order = pyblish.api.ValidatorOrder - 0.49
+
+ hosts = ["traypublisher"]
+ families = ["editorial_pkg"]
+
+ def process(self, instance):
+ editorial_pkg_data = instance.data.get("editorial_pkg")
+ if not editorial_pkg_data:
+ raise PublishValidationError("Editorial package not collected")
+
+ folder_path = editorial_pkg_data["folder_path"]
+
+ otio_path = editorial_pkg_data["otio_path"]
+ if not otio_path:
+ raise PublishValidationError(
+ f"Folder {folder_path} missing otio file")
+
+ resource_paths = editorial_pkg_data["resource_paths"]
+
+ resource_file_names = {os.path.basename(path)
+ for path in resource_paths}
+
+ try:
+ otio_data = opentimelineio.adapters.read_from_file(otio_path)
+ except UnsupportedSchemaError as e:
+ raise PublishValidationError(
+ f"Unsupported schema in otio file '{otio_path}'."
+ "Version of your OpenTimelineIO library is too old."
+ "Please update it to the latest version."
+ f"Current version is '{opentimelineio.__version__}', "
+ "but required is at least 0.16.0."
+ ) from e
+
+ target_urls = self._get_all_target_urls(otio_data)
+ missing_files = set()
+ for target_url in target_urls:
+ target_basename = os.path.basename(target_url)
+ if target_basename not in resource_file_names:
+ missing_files.add(target_basename)
+
+ if missing_files:
+ raise PublishValidationError(
+ f"Otio file contains missing files `{missing_files}`.\n\n"
+ f"Please add them to `{folder_path}` and republish.")
+
+ instance.data["editorial_pkg"]["otio_data"] = otio_data
+
+ def _get_all_target_urls(self, otio_data):
+ target_urls = []
+
+ # Iterate through tracks, clips, or other elements
+ for track in otio_data.tracks:
+ for clip in track:
+ # Check if the clip has a media reference
+ if clip.media_reference is not None:
+ # Access the target_url from the media reference
+ target_url = clip.media_reference.target_url
+ if target_url:
+ target_urls.append(target_url)
+
+ return target_urls
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_existing_version.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/validate_existing_version.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_existing_version.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_filepaths.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_filepaths.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/validate_filepaths.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_filepaths.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_frame_ranges.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/validate_frame_ranges.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_frame_ranges.py
diff --git a/client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py b/server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_online_file.py
similarity index 100%
rename from client/ayon_core/hosts/traypublisher/plugins/publish/validate_online_file.py
rename to server_addon/traypublisher/client/ayon_traypublisher/plugins/publish/validate_online_file.py
diff --git a/client/ayon_core/tools/traypublisher/__init__.py b/server_addon/traypublisher/client/ayon_traypublisher/ui/__init__.py
similarity index 100%
rename from client/ayon_core/tools/traypublisher/__init__.py
rename to server_addon/traypublisher/client/ayon_traypublisher/ui/__init__.py
diff --git a/client/ayon_core/tools/traypublisher/window.py b/server_addon/traypublisher/client/ayon_traypublisher/ui/window.py
similarity index 99%
rename from client/ayon_core/tools/traypublisher/window.py
rename to server_addon/traypublisher/client/ayon_traypublisher/ui/window.py
index 4700e20531..288dac8529 100644
--- a/client/ayon_core/tools/traypublisher/window.py
+++ b/server_addon/traypublisher/client/ayon_traypublisher/ui/window.py
@@ -13,7 +13,6 @@ import qtawesome
from ayon_core.lib import AYONSettingsRegistry, is_running_from_build
from ayon_core.pipeline import install_host
-from ayon_core.hosts.traypublisher.api import TrayPublisherHost
from ayon_core.tools.publisher.control_qt import QtPublisherController
from ayon_core.tools.publisher.window import PublisherWindow
from ayon_core.tools.common_models import ProjectsModel
@@ -24,6 +23,7 @@ from ayon_core.tools.utils import (
ProjectSortFilterProxy,
PROJECT_NAME_ROLE,
)
+from ayon_traypublisher.api import TrayPublisherHost
class TrayPublisherRegistry(AYONSettingsRegistry):
diff --git a/server_addon/traypublisher/package.py b/server_addon/traypublisher/package.py
index 4ca8ae9fd3..c9b94c2b72 100644
--- a/server_addon/traypublisher/package.py
+++ b/server_addon/traypublisher/package.py
@@ -1,3 +1,10 @@
name = "traypublisher"
title = "TrayPublisher"
-version = "0.1.4"
+version = "0.2.1"
+
+client_dir = "ayon_traypublisher"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/server_addon/traypublisher/server/settings/publish_plugins.py b/server_addon/traypublisher/server/settings/publish_plugins.py
index f413c86227..99a0bbf107 100644
--- a/server_addon/traypublisher/server/settings/publish_plugins.py
+++ b/server_addon/traypublisher/server/settings/publish_plugins.py
@@ -1,4 +1,7 @@
-from ayon_server.settings import BaseSettingsModel, SettingsField
+from ayon_server.settings import (
+ BaseSettingsModel,
+ SettingsField,
+)
class ValidatePluginModel(BaseSettingsModel):
@@ -14,6 +17,45 @@ class ValidateFrameRangeModel(ValidatePluginModel):
'my_asset_to_publish.mov')"""
+class ExtractEditorialPckgFFmpegModel(BaseSettingsModel):
+ video_filters: list[str] = SettingsField(
+ default_factory=list,
+ title="Video filters"
+ )
+ audio_filters: list[str] = SettingsField(
+ default_factory=list,
+ title="Audio filters"
+ )
+ input: list[str] = SettingsField(
+ default_factory=list,
+ title="Input arguments"
+ )
+ output: list[str] = SettingsField(
+ default_factory=list,
+ title="Output arguments"
+ )
+
+
+class ExtractEditorialPckgOutputDefModel(BaseSettingsModel):
+ _layout = "expanded"
+ ext: str = SettingsField("", title="Output extension")
+
+ ffmpeg_args: ExtractEditorialPckgFFmpegModel = SettingsField(
+ default_factory=ExtractEditorialPckgFFmpegModel,
+ title="FFmpeg arguments"
+ )
+
+
+class ExtractEditorialPckgConversionModel(BaseSettingsModel):
+ """Set output definition if resource files should be converted."""
+ conversion_enabled: bool = SettingsField(True,
+ title="Conversion enabled")
+ output: ExtractEditorialPckgOutputDefModel = SettingsField(
+ default_factory=ExtractEditorialPckgOutputDefModel,
+ title="Output Definitions",
+ )
+
+
class TrayPublisherPublishPlugins(BaseSettingsModel):
CollectFrameDataFromAssetEntity: ValidatePluginModel = SettingsField(
default_factory=ValidatePluginModel,
@@ -28,6 +70,13 @@ class TrayPublisherPublishPlugins(BaseSettingsModel):
default_factory=ValidatePluginModel,
)
+ ExtractEditorialPckgConversion: ExtractEditorialPckgConversionModel = (
+ SettingsField(
+ default_factory=ExtractEditorialPckgConversionModel,
+ title="Extract Editorial Package Conversion"
+ )
+ )
+
DEFAULT_PUBLISH_PLUGINS = {
"CollectFrameDataFromAssetEntity": {
@@ -44,5 +93,24 @@ DEFAULT_PUBLISH_PLUGINS = {
"enabled": True,
"optional": True,
"active": True
+ },
+ "ExtractEditorialPckgConversion": {
+ "optional": False,
+ "conversion_enabled": True,
+ "output": {
+ "ext": "",
+ "ffmpeg_args": {
+ "video_filters": [],
+ "audio_filters": [],
+ "input": [
+ "-apply_trc gamma22"
+ ],
+ "output": [
+ "-pix_fmt yuv420p",
+ "-crf 18",
+ "-intra"
+ ]
+ }
+ }
}
}
diff --git a/client/ayon_core/hosts/tvpaint/__init__.py b/server_addon/tvpaint/client/ayon_tvpaint/__init__.py
similarity index 77%
rename from client/ayon_core/hosts/tvpaint/__init__.py
rename to server_addon/tvpaint/client/ayon_tvpaint/__init__.py
index b98680f204..2c4a052234 100644
--- a/client/ayon_core/hosts/tvpaint/__init__.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/__init__.py
@@ -1,3 +1,4 @@
+from .version import __version__
from .addon import (
get_launch_script_path,
TVPaintAddon,
@@ -6,6 +7,8 @@ from .addon import (
__all__ = (
+ "__version__",
+
"get_launch_script_path",
"TVPaintAddon",
"TVPAINT_ROOT_DIR",
diff --git a/client/ayon_core/hosts/tvpaint/addon.py b/server_addon/tvpaint/client/ayon_tvpaint/addon.py
similarity index 93%
rename from client/ayon_core/hosts/tvpaint/addon.py
rename to server_addon/tvpaint/client/ayon_tvpaint/addon.py
index 6756b274f9..c98c929a96 100644
--- a/client/ayon_core/hosts/tvpaint/addon.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/addon.py
@@ -1,6 +1,8 @@
import os
from ayon_core.addon import AYONAddon, IHostAddon
+from .version import __version__
+
TVPAINT_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -14,6 +16,7 @@ def get_launch_script_path():
class TVPaintAddon(AYONAddon, IHostAddon):
name = "tvpaint"
+ version = __version__
host_name = "tvpaint"
def add_implementation_envs(self, env, _app):
diff --git a/client/ayon_core/hosts/tvpaint/api/__init__.py b/server_addon/tvpaint/client/ayon_tvpaint/api/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/api/__init__.py
rename to server_addon/tvpaint/client/ayon_tvpaint/api/__init__.py
diff --git a/client/ayon_core/hosts/tvpaint/api/communication_server.py b/server_addon/tvpaint/client/ayon_tvpaint/api/communication_server.py
similarity index 99%
rename from client/ayon_core/hosts/tvpaint/api/communication_server.py
rename to server_addon/tvpaint/client/ayon_tvpaint/api/communication_server.py
index d185bdf685..7ccb49f07e 100644
--- a/client/ayon_core/hosts/tvpaint/api/communication_server.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/api/communication_server.py
@@ -22,7 +22,7 @@ from aiohttp_json_rpc.protocol import (
from aiohttp_json_rpc.exceptions import RpcError
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.setLevel(logging.DEBUG)
diff --git a/client/ayon_core/hosts/tvpaint/api/launch_script.py b/server_addon/tvpaint/client/ayon_tvpaint/api/launch_script.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/api/launch_script.py
rename to server_addon/tvpaint/client/ayon_tvpaint/api/launch_script.py
index bcc92d8b6d..1e23e95572 100644
--- a/client/ayon_core/hosts/tvpaint/api/launch_script.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/api/launch_script.py
@@ -10,7 +10,7 @@ from qtpy import QtWidgets, QtCore, QtGui
from ayon_core import style
from ayon_core.pipeline import install_host
-from ayon_core.hosts.tvpaint.api import (
+from ayon_tvpaint.api import (
TVPaintHost,
CommunicationWrapper,
)
diff --git a/client/ayon_core/hosts/tvpaint/api/lib.py b/server_addon/tvpaint/client/ayon_tvpaint/api/lib.py
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/api/lib.py
rename to server_addon/tvpaint/client/ayon_tvpaint/api/lib.py
diff --git a/client/ayon_core/hosts/tvpaint/api/pipeline.py b/server_addon/tvpaint/client/ayon_tvpaint/api/pipeline.py
similarity index 99%
rename from client/ayon_core/hosts/tvpaint/api/pipeline.py
rename to server_addon/tvpaint/client/ayon_tvpaint/api/pipeline.py
index 6f5c4d49d4..5ec6355138 100644
--- a/client/ayon_core/hosts/tvpaint/api/pipeline.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/api/pipeline.py
@@ -7,8 +7,9 @@ import requests
import ayon_api
import pyblish.api
+from ayon_tvpaint import TVPAINT_ROOT_DIR
+
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.lib import register_event_callback
from ayon_core.pipeline import (
diff --git a/client/ayon_core/hosts/tvpaint/api/plugin.py b/server_addon/tvpaint/client/ayon_tvpaint/api/plugin.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/api/plugin.py
rename to server_addon/tvpaint/client/ayon_tvpaint/api/plugin.py
index e715b959f4..9dd6ae530a 100644
--- a/client/ayon_core/hosts/tvpaint/api/plugin.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/api/plugin.py
@@ -12,7 +12,7 @@ from ayon_core.pipeline.create.creator_plugins import cache_and_get_instances
from .lib import get_layers_data
-SHARED_DATA_KEY = "openpype.tvpaint.instances"
+SHARED_DATA_KEY = "ayon.tvpaint.instances"
class TVPaintCreatorCommon:
@@ -89,6 +89,8 @@ class TVPaintCreatorCommon:
class TVPaintCreator(Creator, TVPaintCreatorCommon):
+ settings_category = "tvpaint"
+
def collect_instances(self):
self._collect_create_instances()
@@ -140,6 +142,8 @@ class TVPaintCreator(Creator, TVPaintCreatorCommon):
class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon):
+ settings_category = "tvpaint"
+
def collect_instances(self):
self._collect_create_instances()
@@ -152,6 +156,7 @@ class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon):
class Loader(LoaderPlugin):
hosts = ["tvpaint"]
+ settings_category = "tvpaint"
@staticmethod
def get_members_from_container(container):
diff --git a/client/ayon_core/hosts/tvpaint/hooks/pre_launch_args.py b/server_addon/tvpaint/client/ayon_tvpaint/hooks/pre_launch_args.py
similarity index 95%
rename from client/ayon_core/hosts/tvpaint/hooks/pre_launch_args.py
rename to server_addon/tvpaint/client/ayon_tvpaint/hooks/pre_launch_args.py
index 691b81e089..8ee91aa0e7 100644
--- a/client/ayon_core/hosts/tvpaint/hooks/pre_launch_args.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/hooks/pre_launch_args.py
@@ -37,6 +37,6 @@ class TvpaintPrelaunchHook(PreLaunchHook):
self.launch_context.launch_args.extend(remainders)
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()
diff --git a/client/ayon_core/hosts/tvpaint/lib.py b/server_addon/tvpaint/client/ayon_tvpaint/lib.py
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/lib.py
rename to server_addon/tvpaint/client/ayon_tvpaint/lib.py
diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/convert_legacy.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/create/convert_legacy.py
index 34fe0ce8f4..e79a6565e8 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/create/convert_legacy.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/convert_legacy.py
@@ -4,8 +4,8 @@ from ayon_core.pipeline.create.creator_plugins import (
ProductConvertorPlugin,
cache_and_get_instances,
)
-from ayon_core.hosts.tvpaint.api.plugin import SHARED_DATA_KEY
-from ayon_core.hosts.tvpaint.api.lib import get_groups_data
+from ayon_tvpaint.api.plugin import SHARED_DATA_KEY
+from ayon_tvpaint.api.lib import get_groups_data
class TVPaintLegacyConverted(ProductConvertorPlugin):
diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_render.py
similarity index 99%
rename from client/ayon_core/hosts/tvpaint/plugins/create/create_render.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_render.py
index dc9c2466e0..2286a4417a 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/create/create_render.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_render.py
@@ -52,11 +52,11 @@ from ayon_core.pipeline.create import (
CreatedInstance,
CreatorError,
)
-from ayon_core.hosts.tvpaint.api.plugin import (
+from ayon_tvpaint.api.plugin import (
TVPaintCreator,
TVPaintAutoCreator,
)
-from ayon_core.hosts.tvpaint.api.lib import (
+from ayon_tvpaint.api.lib import (
get_layers_data,
get_groups_data,
execute_george_through_file,
diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_review.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/plugins/create/create_review.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_review.py
index acb4f0f8d6..6068ffa1d8 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/create/create_review.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_review.py
@@ -1,7 +1,7 @@
import ayon_api
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):
diff --git a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_workfile.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_workfile.py
index f21f41439e..b08f731869 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/create/create_workfile.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/create/create_workfile.py
@@ -1,7 +1,7 @@
import ayon_api
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):
diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_image.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_image.py
similarity index 95%
rename from client/ayon_core/hosts/tvpaint/plugins/load/load_image.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_image.py
index aad8f92d26..18b06c9632 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/load/load_image.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_image.py
@@ -1,6 +1,6 @@
from ayon_core.lib.attribute_definitions import BoolDef
-from ayon_core.hosts.tvpaint.api import plugin
-from ayon_core.hosts.tvpaint.api.lib import execute_george_through_file
+from ayon_tvpaint.api import plugin
+from ayon_tvpaint.api.lib import execute_george_through_file
class ImportImage(plugin.Loader):
@@ -8,6 +8,7 @@ class ImportImage(plugin.Loader):
product_types = {"render", "image", "background", "plate", "review"}
representations = {"*"}
+ settings_category = "tvpaint"
label = "Import Image"
order = 1
diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_reference_image.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_reference_image.py
index a7fcb9f4a4..88bf738999 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/load/load_reference_image.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_reference_image.py
@@ -2,12 +2,12 @@ import collections
from ayon_core.lib.attribute_definitions import BoolDef
from ayon_core.pipeline import registered_host
-from ayon_core.hosts.tvpaint.api import plugin
-from ayon_core.hosts.tvpaint.api.lib import (
+from ayon_tvpaint.api import plugin
+from ayon_tvpaint.api.lib import (
get_layers_data,
execute_george_through_file,
)
-from ayon_core.hosts.tvpaint.api.pipeline import (
+from ayon_tvpaint.api.pipeline import (
write_workfile_metadata,
SECTION_NAME_CONTAINERS,
containerise,
@@ -19,6 +19,7 @@ class LoadImage(plugin.Loader):
product_types = {"render", "image", "background", "plate", "review"}
representations = {"*"}
+ settings_category = "tvpaint"
label = "Load Image"
order = 1
diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_sound.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_sound.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/plugins/load/load_sound.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_sound.py
index 7e8c8022d6..086afba079 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/load/load_sound.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_sound.py
@@ -1,7 +1,7 @@
import os
import tempfile
-from ayon_core.hosts.tvpaint.api import plugin
-from ayon_core.hosts.tvpaint.api.lib import (
+from ayon_tvpaint.api import plugin
+from ayon_tvpaint.api.lib import (
execute_george_through_file,
)
diff --git a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_workfile.py
similarity index 96%
rename from client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_workfile.py
index 07c2d91533..045e22f188 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/load/load_workfile.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/load/load_workfile.py
@@ -10,11 +10,11 @@ from ayon_core.pipeline.workfile import (
get_last_workfile_with_version,
)
from ayon_core.pipeline.template_data import get_template_data_with_names
-from ayon_core.hosts.tvpaint.api import plugin
-from ayon_core.hosts.tvpaint.api.lib import (
+from ayon_tvpaint.api import plugin
+from ayon_tvpaint.api.lib import (
execute_george_through_file,
)
-from ayon_core.hosts.tvpaint.api.pipeline import (
+from ayon_tvpaint.api.pipeline import (
get_current_workfile_context,
)
from ayon_core.pipeline.version_start import get_versioning_start
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_instance_frames.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_instance_frames.py
index 5f134a0cd0..a9e69166d7 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_instance_frames.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_instance_frames.py
@@ -14,6 +14,8 @@ class CollectOutputFrameRange(pyblish.api.InstancePlugin):
hosts = ["tvpaint"]
families = ["review", "render"]
+ settings_category = "tvpaint"
+
def process(self, instance):
folder_entity = instance.data.get("folderEntity")
if not folder_entity:
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_render_instances.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_render_instances.py
similarity index 99%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/collect_render_instances.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_render_instances.py
index 596d257f22..00af624700 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_render_instances.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_render_instances.py
@@ -9,6 +9,7 @@ class CollectRenderInstances(pyblish.api.InstancePlugin):
hosts = ["tvpaint"]
families = ["render", "review"]
+ settings_category = "tvpaint"
ignore_render_pass_transparency = False
def process(self, instance):
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_workfile.py
similarity index 96%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_workfile.py
index a9e9db3872..27de086a46 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_workfile.py
@@ -9,6 +9,8 @@ class CollectWorkfile(pyblish.api.InstancePlugin):
hosts = ["tvpaint"]
families = ["workfile"]
+ settings_category = "tvpaint"
+
def process(self, instance):
context = instance.context
current_file = context.data["currentFile"]
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_workfile_data.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_workfile_data.py
index 3155773bda..a34a718ff5 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/collect_workfile_data.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/collect_workfile_data.py
@@ -4,13 +4,13 @@ import tempfile
import pyblish.api
-from ayon_core.hosts.tvpaint.api.lib import (
+from ayon_tvpaint.api.lib import (
execute_george,
execute_george_through_file,
get_layers_data,
get_groups_data,
)
-from ayon_core.hosts.tvpaint.api.pipeline import (
+from ayon_tvpaint.api.pipeline import (
SECTION_NAME_CONTEXT,
SECTION_NAME_INSTANCES,
SECTION_NAME_CONTAINERS,
@@ -58,6 +58,8 @@ class CollectWorkfileData(pyblish.api.ContextPlugin):
hosts = ["tvpaint"]
actions = [ResetTVPaintWorkfileMetadata]
+ settings_category = "tvpaint"
+
def process(self, context):
current_project_id = execute_george("tv_projectcurrentid")
execute_george("tv_projectselect {}".format(current_project_id))
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_convert_to_exr.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/extract_convert_to_exr.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/extract_convert_to_exr.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/extract_convert_to_exr.py
index d1bc68ef35..020ebc1a89 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_convert_to_exr.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/extract_convert_to_exr.py
@@ -23,6 +23,8 @@ class ExtractConvertToEXR(pyblish.api.InstancePlugin):
hosts = ["tvpaint"]
families = ["render"]
+ settings_category = "tvpaint"
+
enabled = False
# Replace source PNG files or just add
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/extract_sequence.py
similarity index 99%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/extract_sequence.py
index fe5e148b7b..86c20c6528 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/extract_sequence.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/extract_sequence.py
@@ -10,13 +10,13 @@ from ayon_core.pipeline.publish import (
KnownPublishError,
get_publish_instance_families,
)
-from ayon_core.hosts.tvpaint.api.lib import (
+from ayon_tvpaint.api.lib import (
execute_george,
execute_george_through_file,
get_layers_pre_post_behavior,
get_layers_exposure_frames,
)
-from ayon_core.hosts.tvpaint.lib import (
+from ayon_tvpaint.lib import (
calculate_layers_extraction_data,
get_frame_filename_template,
fill_reference_frames,
@@ -31,6 +31,8 @@ class ExtractSequence(pyblish.api.InstancePlugin):
hosts = ["tvpaint"]
families = ["review", "render"]
+ settings_category = "tvpaint"
+
# Modifiable with settings
review_bg = [255, 255, 255, 1.0]
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_asset_name.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_asset_name.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_layers_visibility.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_layers_visibility.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_marks.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_marks.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_marks.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_marks.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_missing_layer_names.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_missing_layer_names.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_render_layer_group.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_render_layer_group.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_render_layer_group.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_render_layer_group.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_render_pass_group.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_render_pass_group.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_scene_settings.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_scene_settings.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_start_frame.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_start_frame.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_workfile_metadata.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_workfile_metadata.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_workfile_project_name.xml
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/help/validate_workfile_project_name.xml
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/increment_workfile_version.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/increment_workfile_version.py
similarity index 95%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/increment_workfile_version.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/increment_workfile_version.py
index 5dd6110bc7..601d276b97 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/increment_workfile_version.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/increment_workfile_version.py
@@ -12,6 +12,8 @@ class IncrementWorkfileVersion(pyblish.api.ContextPlugin):
optional = True
hosts = ["tvpaint"]
+ settings_category = "tvpaint"
+
def process(self, context):
assert all(result["success"] for result in context.data["results"]), (
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_asset_name.py
similarity index 96%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_asset_name.py
index 764c090720..8763c005dc 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_asset_name.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_asset_name.py
@@ -3,7 +3,7 @@ from ayon_core.pipeline import (
PublishXmlValidationError,
OptionalPyblishPluginMixin,
)
-from ayon_core.hosts.tvpaint.api.pipeline import (
+from ayon_tvpaint.api.pipeline import (
list_instances,
write_instances,
)
@@ -48,6 +48,8 @@ class ValidateAssetName(
hosts = ["tvpaint"]
actions = [FixFolderPaths]
+ settings_category = "tvpaint"
+
def process(self, context):
if not self.is_active(context.data):
return
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_duplicated_layer_names.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_duplicated_layer_names.py
index aab0557bdd..be4dc0f123 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_duplicated_layer_names.py
@@ -9,6 +9,8 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["renderPass"]
+ settings_category = "tvpaint"
+
def process(self, instance):
# Prepare layers
layers_by_name = instance.context.data["layersByName"]
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_layers_visibility.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_layers_visibility.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_layers_visibility.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_layers_visibility.py
index 1bcdf7baa1..f58b8a6973 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_layers_visibility.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_layers_visibility.py
@@ -10,6 +10,8 @@ class ValidateLayersVisiblity(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["review", "render"]
+ settings_category = "tvpaint"
+
def process(self, instance):
layers = instance.data.get("layers")
# Instance have empty layers
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_marks.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_marks.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_marks.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_marks.py
index 6bfbe840bb..0911beb4e8 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_marks.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_marks.py
@@ -5,7 +5,7 @@ from ayon_core.pipeline import (
PublishXmlValidationError,
OptionalPyblishPluginMixin,
)
-from ayon_core.hosts.tvpaint.api.lib import execute_george
+from ayon_tvpaint.api.lib import execute_george
class ValidateMarksRepair(pyblish.api.Action):
@@ -41,6 +41,8 @@ class ValidateMarks(
optional = True
actions = [ValidateMarksRepair]
+ settings_category = "tvpaint"
+
@staticmethod
def get_expected_data(context):
scene_mark_in = context.data["sceneMarkIn"]
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_missing_layer_names.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_missing_layer_names.py
index 3fc80f6e78..f340d3c10d 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_missing_layer_names.py
@@ -9,6 +9,8 @@ class ValidateMissingLayers(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
families = ["renderPass"]
+ settings_category = "tvpaint"
+
def process(self, instance):
# Prepare layers
layers_by_name = instance.context.data["layersByName"]
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_layer_group.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_render_layer_group.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_layer_group.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_render_layer_group.py
index 0e97a01de2..b20ea3cac6 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_layer_group.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_render_layer_group.py
@@ -12,6 +12,8 @@ class ValidateRenderLayerGroups(pyblish.api.ContextPlugin):
label = "Validate Render Layers Group"
order = pyblish.api.ValidatorOrder + 0.1
+ settings_category = "tvpaint"
+
def process(self, context):
# Prepare layers
render_layers_by_group_id = collections.defaultdict(list)
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_pass_group.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_render_pass_group.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_pass_group.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_render_pass_group.py
index 874af38dd4..3d00fd031f 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_render_pass_group.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_render_pass_group.py
@@ -13,6 +13,8 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder + 0.1
families = ["renderPass"]
+ settings_category = "tvpaint"
+
def process(self, instance):
# Prepare layers
layers_data = instance.context.data["layersData"]
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_scene_settings.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_scene_settings.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_scene_settings.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_scene_settings.py
index 5e42b5ab2f..8bad5c43c8 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_scene_settings.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_scene_settings.py
@@ -16,6 +16,8 @@ class ValidateProjectSettings(
label = "Validate Scene Settings"
order = pyblish.api.ValidatorOrder
+
+ settings_category = "tvpaint"
optional = True
def process(self, context):
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_start_frame.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_start_frame.py
similarity index 92%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_start_frame.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_start_frame.py
index fea64bd6a8..9669acf1b5 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_start_frame.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_start_frame.py
@@ -3,7 +3,7 @@ from ayon_core.pipeline import (
PublishXmlValidationError,
OptionalPyblishPluginMixin,
)
-from ayon_core.hosts.tvpaint.api.lib import execute_george
+from ayon_tvpaint.api.lib import execute_george
class RepairStartFrame(pyblish.api.Action):
@@ -27,6 +27,8 @@ class ValidateStartFrame(
order = pyblish.api.ValidatorOrder
hosts = ["tvpaint"]
actions = [RepairStartFrame]
+
+ settings_category = "tvpaint"
optional = True
def process(self, context):
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_workfile_metadata.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_workfile_metadata.py
index 1d9954d051..34c02c78ed 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_workfile_metadata.py
@@ -31,6 +31,8 @@ class ValidateWorkfileMetadata(pyblish.api.ContextPlugin):
actions = [ValidateWorkfileMetadataRepair]
+ settings_category = "tvpaint"
+
required_keys = {"project_name", "folder_path", "task_name"}
def process(self, context):
diff --git a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_workfile_project_name.py
similarity index 98%
rename from client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py
rename to server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_workfile_project_name.py
index 5b42842717..868c7d44fc 100644
--- a/client/ayon_core/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/plugins/publish/validate_workfile_project_name.py
@@ -12,6 +12,8 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin):
label = "Validate Workfile Project Name"
order = pyblish.api.ValidatorOrder
+ settings_category = "tvpaint"
+
def process(self, context):
workfile_context = context.data.get("workfile_context")
# If workfile context is missing than project is matching to
diff --git a/client/ayon_core/hosts/tvpaint/resources/template.tvpp b/server_addon/tvpaint/client/ayon_tvpaint/resources/template.tvpp
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/resources/template.tvpp
rename to server_addon/tvpaint/client/ayon_tvpaint/resources/template.tvpp
diff --git a/client/ayon_core/hosts/tvpaint/tvpaint_plugin/__init__.py b/server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/tvpaint_plugin/__init__.py
rename to server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/__init__.py
diff --git a/client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt b/server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt
rename to server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt
diff --git a/client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/README.md b/server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/README.md
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/README.md
rename to server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/README.md
diff --git a/client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp b/server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/library.cpp
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp
rename to server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/library.cpp
diff --git a/client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/library.def b/server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/library.def
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_code/library.def
rename to server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_code/library.def
diff --git a/client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll b/server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll
rename to server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll
diff --git a/client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll b/server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll
rename to server_addon/tvpaint/client/ayon_tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll
diff --git a/server_addon/tvpaint/client/ayon_tvpaint/version.py b/server_addon/tvpaint/client/ayon_tvpaint/version.py
new file mode 100644
index 0000000000..03419cd02e
--- /dev/null
+++ b/server_addon/tvpaint/client/ayon_tvpaint/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring AYON addon 'tvpaint' version."""
+__version__ = "0.2.1"
diff --git a/client/ayon_core/hosts/tvpaint/worker/__init__.py b/server_addon/tvpaint/client/ayon_tvpaint/worker/__init__.py
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/worker/__init__.py
rename to server_addon/tvpaint/client/ayon_tvpaint/worker/__init__.py
diff --git a/client/ayon_core/hosts/tvpaint/worker/init_file.tvpp b/server_addon/tvpaint/client/ayon_tvpaint/worker/init_file.tvpp
similarity index 100%
rename from client/ayon_core/hosts/tvpaint/worker/init_file.tvpp
rename to server_addon/tvpaint/client/ayon_tvpaint/worker/init_file.tvpp
diff --git a/client/ayon_core/hosts/tvpaint/worker/worker.py b/server_addon/tvpaint/client/ayon_tvpaint/worker/worker.py
similarity index 97%
rename from client/ayon_core/hosts/tvpaint/worker/worker.py
rename to server_addon/tvpaint/client/ayon_tvpaint/worker/worker.py
index 3d9b1ef2b8..aa79fd442c 100644
--- a/client/ayon_core/hosts/tvpaint/worker/worker.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/worker/worker.py
@@ -5,11 +5,11 @@ import tempfile
import shutil
import asyncio
-from ayon_core.hosts.tvpaint.api.communication_server import (
+from ayon_tvpaint.api.communication_server import (
BaseCommunicator,
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
diff --git a/client/ayon_core/hosts/tvpaint/worker/worker_job.py b/server_addon/tvpaint/client/ayon_tvpaint/worker/worker_job.py
similarity index 99%
rename from client/ayon_core/hosts/tvpaint/worker/worker_job.py
rename to server_addon/tvpaint/client/ayon_tvpaint/worker/worker_job.py
index f111ed369a..db91010c47 100644
--- a/client/ayon_core/hosts/tvpaint/worker/worker_job.py
+++ b/server_addon/tvpaint/client/ayon_tvpaint/worker/worker_job.py
@@ -256,7 +256,7 @@ class CollectSceneData(BaseCommand):
name = "collect_scene_data"
def execute(self):
- from ayon_core.hosts.tvpaint.api.lib import (
+ from ayon_tvpaint.api.lib import (
get_layers_data,
get_groups_data,
get_layers_pre_post_behavior,
diff --git a/server_addon/tvpaint/package.py b/server_addon/tvpaint/package.py
index 2be3164f4a..eddb112b16 100644
--- a/server_addon/tvpaint/package.py
+++ b/server_addon/tvpaint/package.py
@@ -1,3 +1,9 @@
name = "tvpaint"
title = "TVPaint"
-version = "0.1.2"
+version = "0.2.1"
+client_dir = "ayon_tvpaint"
+
+ayon_required_addons = {
+ "core": ">0.3.2",
+}
+ayon_compatible_addons = {}
diff --git a/server_addon/tvpaint/pyproject.toml b/server_addon/tvpaint/pyproject.toml
new file mode 100644
index 0000000000..46d0611d74
--- /dev/null
+++ b/server_addon/tvpaint/pyproject.toml
@@ -0,0 +1,6 @@
+[project]
+name="tvpaint"
+description="AYON TVPaint addon."
+
+[ayon.runtimeDependencies]
+aiohttp_json_rpc = "*"