Merge pull request #113 from ynput/enhancement/OP-8264_Use-productName-n-productType

Chore: Product name and type
This commit is contained in:
Jakub Trllo 2024-02-28 13:40:57 +01:00 committed by GitHub
commit 2a0f68aa01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
419 changed files with 4342 additions and 4544 deletions

View file

@ -11,7 +11,7 @@ from ayon_core.pipeline import (
from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances
from ayon_core.hosts.aftereffects.api.lib import set_settings
from ayon_core.lib import prepare_template_data
from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS
from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS
class RenderCreator(Creator):
@ -22,7 +22,7 @@ class RenderCreator(Creator):
"""
identifier = "render"
label = "Render"
family = "render"
product_type = "render"
description = "Render creator"
create_allow_context_change = True
@ -31,7 +31,7 @@ class RenderCreator(Creator):
mark_for_review = True
force_setting_values = True
def create(self, subset_name_from_ui, data, pre_create_data):
def create(self, product_name, data, pre_create_data):
stub = api.get_stub() # only after After Effects is up
try:
@ -58,33 +58,37 @@ class RenderCreator(Creator):
len(comps) > 1)
for comp in comps:
composition_name = re.sub(
"[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS),
"[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS),
"",
comp.name
)
if use_composition_name:
if "{composition}" not in subset_name_from_ui.lower():
subset_name_from_ui += "{Composition}"
if "{composition}" not in product_name.lower():
product_name += "{Composition}"
dynamic_fill = prepare_template_data({"composition":
composition_name})
subset_name = subset_name_from_ui.format(**dynamic_fill)
comp_product_name = product_name.format(**dynamic_fill)
data["composition_name"] = composition_name
else:
subset_name = subset_name_from_ui
subset_name = re.sub(r"\{composition\}", '', subset_name,
flags=re.IGNORECASE)
comp_product_name = re.sub(
r"\{composition\}",
"",
product_name,
flags=re.IGNORECASE
)
for inst in self.create_context.instances:
if subset_name == inst.subset_name:
if comp_product_name == inst.product_name:
raise CreatorError("{} already exists".format(
inst.subset_name))
inst.product_name))
data["members"] = [comp.id]
data["orig_comp_name"] = composition_name
new_instance = CreatedInstance(self.family, subset_name, data,
self)
new_instance = CreatedInstance(
self.product_type, comp_product_name, data, self
)
if "farm" in pre_create_data:
use_farm = pre_create_data["farm"]
new_instance.creator_attributes["farm"] = use_farm
@ -96,7 +100,7 @@ class RenderCreator(Creator):
new_instance.data_to_store())
self._add_instance_to_context(new_instance)
stub.rename_item(comp.id, subset_name)
stub.rename_item(comp.id, comp_product_name)
if self.force_setting_values:
set_settings(True, True, [comp.id], print_msg=False)
@ -107,7 +111,7 @@ class RenderCreator(Creator):
"selected by default.",
default=True, label="Use selection"),
BoolDef("use_composition_name",
label="Use composition name in subset"),
label="Use composition name in product"),
UISeparatorDef(),
BoolDef("farm", label="Render on farm"),
BoolDef(
@ -133,9 +137,14 @@ class RenderCreator(Creator):
def collect_instances(self):
for instance_data in cache_and_get_instances(self):
# legacy instances have family=='render' or 'renderLocal', use them
creator_id = (instance_data.get("creator_identifier") or
instance_data.get("family", '').replace("Local", ''))
# legacy instances have product_type=='render' or 'renderLocal', use them
creator_id = instance_data.get("creator_identifier")
if not creator_id:
# NOTE this is for backwards compatibility but probably can be
# removed
creator_id = instance_data.get("family", "")
creator_id = creator_id.replace("Local", "")
if creator_id == self.identifier:
instance_data = self._handle_legacy(instance_data)
instance = CreatedInstance.from_existing(
@ -147,10 +156,10 @@ class RenderCreator(Creator):
for created_inst, _changes in update_list:
api.get_stub().imprint(created_inst.get("instance_id"),
created_inst.data_to_store())
subset_change = _changes.get("subset")
if subset_change:
name_change = _changes.get("productName")
if name_change:
api.get_stub().rename_item(created_inst.data["members"][0],
subset_change.new_value)
name_change.new_value)
def remove_instances(self, instances):
"""Removes metadata and renames to original comp name if available."""
@ -183,15 +192,15 @@ class RenderCreator(Creator):
def get_detail_description(self):
return """Creator for Render instances
Main publishable item in AfterEffects will be of `render` family.
Main publishable item in AfterEffects will be of `render` product type.
Result of this item (instance) is picture sequence or video that could
be a final delivery product or loaded and used in another DCCs.
Select single composition and create instance of 'render' family or
turn off 'Use selection' to create instance for all compositions.
Select single composition and create instance of 'render' product type
or turn off 'Use selection' to create instance for all compositions.
'Use composition name in subset' allows to explicitly add composition
name into created subset name.
'Use composition name in product' allows to explicitly add composition
name into created product name.
Position of composition name could be set in
`project_settings/global/tools/creator/product_name_profiles` with
@ -201,15 +210,16 @@ class RenderCreator(Creator):
be handled at same time.
If {composition} placeholder is not us 'product_name_profiles'
composition name will be capitalized and set at the end of subset name
if necessary.
composition name will be capitalized and set at the end of
product name if necessary.
If composition name should be used, it will be cleaned up of characters
that would cause an issue in published file names.
"""
def get_dynamic_data(self, variant, task_name, asset_doc,
project_name, host_name, instance):
def get_dynamic_data(
self, project_name, asset_doc, task_name, variant, host_name, instance
):
dynamic_data = {}
if instance is not None:
composition_name = instance.get("composition_name")
@ -234,9 +244,9 @@ class RenderCreator(Creator):
instance_data["task"] = self.create_context.get_current_task_name()
if not instance_data.get("creator_attributes"):
is_old_farm = instance_data["family"] != "renderLocal"
is_old_farm = instance_data.get("family") != "renderLocal"
instance_data["creator_attributes"] = {"farm": is_old_farm}
instance_data["family"] = self.family
instance_data["productType"] = self.product_type
if instance_data["creator_attributes"].get("mark_for_review") is None:
instance_data["creator_attributes"]["mark_for_review"] = True

View file

@ -9,7 +9,7 @@ from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances
class AEWorkfileCreator(AutoCreator):
identifier = "workfile"
family = "workfile"
product_type = "workfile"
default_variant = "Main"
@ -20,9 +20,9 @@ class AEWorkfileCreator(AutoCreator):
for instance_data in cache_and_get_instances(self):
creator_id = instance_data.get("creator_identifier")
if creator_id == self.identifier:
subset_name = instance_data["subset"]
product_name = instance_data["productName"]
instance = CreatedInstance(
self.family, subset_name, instance_data, self
self.product_type, product_name, instance_data, self
)
self._add_instance_to_context(instance)
@ -33,7 +33,7 @@ class AEWorkfileCreator(AutoCreator):
def create(self, options=None):
existing_instance = None
for instance in self.create_context.instances:
if instance.family == self.family:
if instance.product_type == self.product_type:
existing_instance = instance
break
@ -49,9 +49,12 @@ class AEWorkfileCreator(AutoCreator):
if existing_instance is None:
asset_doc = get_asset_by_name(project_name, asset_name)
subset_name = self.get_subset_name(
self.default_variant, task_name, asset_doc,
project_name, host_name
product_name = self.get_product_name(
project_name,
asset_doc,
task_name,
self.default_variant,
host_name,
)
data = {
"folderPath": asset_name,
@ -59,12 +62,16 @@ class AEWorkfileCreator(AutoCreator):
"variant": self.default_variant,
}
data.update(self.get_dynamic_data(
self.default_variant, task_name, asset_doc,
project_name, host_name, None
project_name,
asset_doc,
task_name,
self.default_variant,
host_name,
None,
))
new_instance = CreatedInstance(
self.family, subset_name, data, self
self.product_type, product_name, data, self
)
self._add_instance_to_context(new_instance)
@ -76,10 +83,13 @@ class AEWorkfileCreator(AutoCreator):
or existing_instance["task"] != task_name
):
asset_doc = get_asset_by_name(project_name, asset_name)
subset_name = self.get_subset_name(
self.default_variant, task_name, asset_doc,
project_name, host_name
product_name = self.get_product_name(
project_name,
asset_doc,
task_name,
self.default_variant,
host_name,
)
existing_instance["folderPath"] = asset_name
existing_instance["task"] = task_name
existing_instance["subset"] = subset_name
existing_instance["productName"] = product_name

View file

@ -11,7 +11,7 @@ from ayon_core.hosts.aftereffects.api.lib import (
class BackgroundLoader(api.AfterEffectsLoader):
"""
Load images from Background family
Load images from Background product type
Creates for each background separate folder with all imported images
from background json AND automatically created composition with layers,
each layer for separate image.

View file

@ -60,8 +60,8 @@ class CollectAERender(publish.AbstractCollectRender):
if not inst.data.get("active", True):
continue
family = inst.data["family"]
if family not in ["render", "renderLocal"]: # legacy
product_type = inst.data["productType"]
if product_type not in ["render", "renderLocal"]: # legacy
continue
comp_id = int(inst.data["members"][0])
@ -81,29 +81,32 @@ class CollectAERender(publish.AbstractCollectRender):
fps = comp_info.frameRate
# TODO add resolution when supported by extension
task_name = inst.data.get("task") # legacy
task_name = inst.data.get("task")
render_q = CollectAERender.get_stub().get_render_info(comp_id)
if not render_q:
raise ValueError("No file extension set in Render Queue")
render_item = render_q[0]
product_type = "render"
instance_families = inst.data.get("families", [])
subset_name = inst.data["subset"]
instance_families.append(product_type)
product_name = inst.data["productName"]
instance = AERenderInstance(
family="render",
productType=product_type,
family=product_type,
families=instance_families,
version=version,
time="",
source=current_file,
label="{} - {}".format(subset_name, family),
subset=subset_name,
label="{} - {}".format(product_name, product_type),
productName=product_name,
folderPath=inst.data["folderPath"],
task=task_name,
attachTo=False,
setMembers='',
publish=True,
name=subset_name,
name=product_name,
resolutionWidth=render_item.width,
resolutionHeight=render_item.height,
pixelAspect=1,
@ -176,7 +179,7 @@ class CollectAERender(publish.AbstractCollectRender):
if "#" not in file_name: # single frame (mov)W
path = os.path.join(base_dir, "{}_{}_{}.{}".format(
render_instance.folderPath,
render_instance.subset,
render_instance.productName,
version_str,
ext
))
@ -185,7 +188,7 @@ class CollectAERender(publish.AbstractCollectRender):
for frame in range(start, end + 1):
path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format(
render_instance.folderPath,
render_instance.subset,
render_instance.productName,
version_str,
str(frame).zfill(self.padding_width),
ext

View file

@ -3,7 +3,7 @@ Requires:
None
Provides:
instance -> family ("review")
instance -> families ("review")
"""
import pyblish.api

View file

@ -2,9 +2,6 @@ import os
import pyblish.api
from ayon_core.client import get_asset_name_identifier
from ayon_core.pipeline.create import get_subset_name
class CollectWorkfile(pyblish.api.ContextPlugin):
""" Adds the AE render instances """
@ -15,86 +12,24 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
default_variant = "Main"
def process(self, context):
existing_instance = None
workfile_instance = None
for instance in context:
if instance.data["family"] == "workfile":
self.log.debug("Workfile instance found, won't create new")
existing_instance = instance
if instance.data["productType"] == "workfile":
self.log.debug("Workfile instance found")
workfile_instance = instance
break
current_file = context.data["currentFile"]
staging_dir = os.path.dirname(current_file)
scene_file = os.path.basename(current_file)
if existing_instance is None: # old publish
instance = self._get_new_instance(context, scene_file)
else:
instance = existing_instance
if workfile_instance is None:
self.log.debug("Workfile instance not found. Skipping")
return
# creating representation
representation = {
'name': 'aep',
'ext': 'aep',
'files': scene_file,
workfile_instance.data["representations"].append({
"name": "aep",
"ext": "aep",
"files": scene_file,
"stagingDir": staging_dir,
}
if not instance.data.get("representations"):
instance.data["representations"] = []
instance.data["representations"].append(representation)
instance.data["publish"] = instance.data["active"] # for DL
def _get_new_instance(self, context, scene_file):
task = context.data["task"]
version = context.data["version"]
asset_entity = context.data["assetEntity"]
project_entity = context.data["projectEntity"]
folder_path = get_asset_name_identifier(asset_entity)
instance_data = {
"active": True,
"folderPath": folder_path,
"task": task,
"frameStart": context.data['frameStart'],
"frameEnd": context.data['frameEnd'],
"handleStart": context.data['handleStart'],
"handleEnd": context.data['handleEnd'],
"fps": asset_entity["data"]["fps"],
"resolutionWidth": asset_entity["data"].get(
"resolutionWidth",
project_entity["data"]["resolutionWidth"]),
"resolutionHeight": asset_entity["data"].get(
"resolutionHeight",
project_entity["data"]["resolutionHeight"]),
"pixelAspect": 1,
"step": 1,
"version": version
}
# workfile instance
family = "workfile"
subset = get_subset_name(
family,
self.default_variant,
context.data["anatomyData"]["task"]["name"],
context.data["assetEntity"],
context.data["anatomyData"]["project"]["name"],
host_name=context.data["hostName"],
project_settings=context.data["project_settings"]
)
# Create instance
instance = context.create_instance(subset)
# creating instance data
instance.data.update({
"subset": subset,
"label": scene_file,
"family": family,
"families": [family],
"representations": list()
})
instance.data.update(instance_data)
return instance

View file

@ -3,9 +3,9 @@
<error id="main">
<title>Subset context</title>
<description>
## Invalid subset context
## Invalid product context
Context of the given subset doesn't match your current scene.
Context of the given product doesn't match your current scene.
### How to repair?
@ -15,7 +15,7 @@ You can fix this with "repair" button on the right and refresh Publish at the bo
### __Detailed Info__ (optional)
This might happen if you are reuse old workfile and open it in different context.
(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.)
(Eg. you created product name "renderCompositingDefault" from folder "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" asset stayed in the workfile.)
</detail>
</error>
</root>

View file

@ -1,54 +0,0 @@
import json
import pyblish.api
from ayon_core.hosts.aftereffects.api import AfterEffectsHost
class PreCollectRender(pyblish.api.ContextPlugin):
"""
Checks if render instance is of old type, adds to families to both
existing collectors work same way.
Could be removed in the future when no one uses old publish.
"""
label = "PreCollect Render"
order = pyblish.api.CollectorOrder + 0.400
hosts = ["aftereffects"]
family_remapping = {
"render": ("render.farm", "farm"), # (family, label)
"renderLocal": ("render.local", "local")
}
def process(self, context):
if context.data.get("newPublishing"):
self.log.debug("Not applicable for New Publisher, skip")
return
for inst in AfterEffectsHost().list_instances():
if inst.get("creator_attributes"):
raise ValueError("Instance created in New publisher, "
"cannot be published in Pyblish.\n"
"Please publish in New Publisher "
"or recreate instances with legacy Creators")
if inst["family"] not in self.family_remapping.keys():
continue
if not inst["members"]:
raise ValueError("Couldn't find id, unable to publish. " +
"Please recreate instance.")
instance = context.create_instance(inst["subset"])
inst["families"] = [self.family_remapping[inst["family"]][0]]
instance.data.update(inst)
self._debug_log(instance)
def _debug_log(self, instance):
def _default_json(value):
return str(value)
self.log.info(
json.dumps(instance.data, indent=4, default=_default_json)
)

View file

@ -30,13 +30,13 @@ VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx"]
def prepare_scene_name(
asset: str, subset: str, namespace: Optional[str] = None
folder_name: str, product_name: str, namespace: Optional[str] = None
) -> str:
"""Return a consistent name for an asset."""
name = f"{asset}"
name = f"{folder_name}"
if namespace:
name = f"{name}_{namespace}"
name = f"{name}_{subset}"
name = f"{name}_{product_name}"
# Blender name for a collection or object cannot be longer than 63
# characters. If the name is longer, it will raise an error.
@ -47,7 +47,7 @@ def prepare_scene_name(
def get_unique_number(
asset: str, subset: str
folder_name: str, product_name: str
) -> str:
"""Return a unique number based on the asset name."""
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
@ -64,10 +64,10 @@ def get_unique_number(
if c.get(AVALON_PROPERTY)}
container_names = obj_group_names.union(coll_group_names)
count = 1
name = f"{asset}_{count:0>2}_{subset}"
name = f"{folder_name}_{count:0>2}_{product_name}"
while name in container_names:
count += 1
name = f"{asset}_{count:0>2}_{subset}"
name = f"{folder_name}_{count:0>2}_{product_name}"
return f"{count:0>2}"
@ -161,24 +161,22 @@ class BaseCreator(Creator):
create_as_asset_group = False
@staticmethod
def cache_subsets(shared_data):
def cache_instance_data(shared_data):
"""Cache instances for Creators shared data.
Create `blender_cached_subsets` key when needed in shared data and
Create `blender_cached_instances` key when needed in shared data and
fill it with all collected instances from the scene under its
respective creator identifiers.
If legacy instances are detected in the scene, create
`blender_cached_legacy_subsets` key and fill it with
all legacy subsets from this family as a value. # key or value?
`blender_cached_legacy_instances` key and fill it with
all legacy products from this family as a value. # key or value?
Args:
shared_data(Dict[str, Any]): Shared data.
Return:
Dict[str, Any]: Shared data with cached subsets.
"""
if not shared_data.get('blender_cached_subsets'):
if not shared_data.get('blender_cached_instances'):
cache = {}
cache_legacy = {}
@ -210,19 +208,19 @@ class BaseCreator(Creator):
# Legacy creator instance
cache_legacy.setdefault(family, []).append(obj_or_col)
shared_data["blender_cached_subsets"] = cache
shared_data["blender_cached_legacy_subsets"] = cache_legacy
shared_data["blender_cached_instances"] = cache
shared_data["blender_cached_legacy_instances"] = cache_legacy
return shared_data
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
"""Override abstract method from Creator.
Create new instance and store it.
Args:
subset_name(str): Subset name of created instance.
product_name(str): Subset name of created instance.
instance_data(dict): Instance base data.
pre_create_data(dict): Data based on pre creation attributes.
Those may affect how creator works.
@ -236,7 +234,7 @@ class BaseCreator(Creator):
# Create asset group
asset_name = instance_data["folderPath"].split("/")[-1]
name = prepare_scene_name(asset_name, subset_name)
name = prepare_scene_name(asset_name, product_name)
if self.create_as_asset_group:
# Create instance as empty
instance_node = bpy.data.objects.new(name=name, object_data=None)
@ -247,10 +245,10 @@ class BaseCreator(Creator):
instance_node = bpy.data.collections.new(name=name)
instances.children.link(instance_node)
self.set_instance_data(subset_name, instance_data)
self.set_instance_data(product_name, instance_data)
instance = CreatedInstance(
self.family, subset_name, instance_data, self
self.product_type, product_name, instance_data, self
)
instance.transient_data["instance_node"] = instance_node
self._add_instance_to_context(instance)
@ -263,18 +261,18 @@ class BaseCreator(Creator):
"""Override abstract method from BaseCreator.
Collect existing instances related to this creator plugin."""
# Cache subsets in shared data
self.cache_subsets(self.collection_shared_data)
# Cache instances in shared data
self.cache_instance_data(self.collection_shared_data)
# Get cached subsets
cached_subsets = self.collection_shared_data.get(
"blender_cached_subsets"
# Get cached instances
cached_instances = self.collection_shared_data.get(
"blender_cached_instances"
)
if not cached_subsets:
if not cached_instances:
return
# Process only instances that were created by this creator
for instance_node in cached_subsets.get(self.identifier, []):
for instance_node in cached_instances.get(self.identifier, []):
property = instance_node.get(AVALON_PROPERTY)
# Create instance object from existing data
instance = CreatedInstance.from_existing(
@ -306,16 +304,17 @@ class BaseCreator(Creator):
)
return
# Rename the instance node in the scene if subset or asset changed.
# Rename the instance node in the scene if product
# or folder changed.
# Do not rename the instance if the family is workfile, as the
# workfile instance is included in the AVALON_CONTAINER collection.
if (
"subset" in changes.changed_keys
"productName" in changes.changed_keys
or "folderPath" in changes.changed_keys
) and created_instance.family != "workfile":
) and created_instance.product_type != "workfile":
asset_name = data["folderPath"].split("/")[-1]
name = prepare_scene_name(
asset=asset_name, subset=data["subset"]
asset_name, data["productName"]
)
node.name = name
@ -341,13 +340,13 @@ class BaseCreator(Creator):
def set_instance_data(
self,
subset_name: str,
product_name: str,
instance_data: dict
):
"""Fill instance data with required items.
Args:
subset_name(str): Subset name of created instance.
product_name(str): Subset name of created instance.
instance_data(dict): Instance base data.
instance_node(bpy.types.ID): Instance node in blender scene.
"""
@ -358,7 +357,7 @@ class BaseCreator(Creator):
{
"id": AVALON_INSTANCE_ID,
"creator_identifier": self.identifier,
"subset": subset_name,
"productName": product_name,
}
)
@ -466,14 +465,14 @@ class AssetLoader(LoaderPlugin):
filepath = self.filepath_from_context(context)
assert Path(filepath).exists(), f"{filepath} doesn't exist."
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
unique_number = get_unique_number(
asset, subset
folder_name, product_name
)
namespace = namespace or f"{asset}_{unique_number}"
namespace = namespace or f"{folder_name}_{unique_number}"
name = name or prepare_scene_name(
asset, subset, unique_number
folder_name, product_name, unique_number
)
nodes = self.process_asset(
@ -499,10 +498,10 @@ class AssetLoader(LoaderPlugin):
# loader=self.__class__.__name__,
# )
# asset = context["asset"]["name"]
# subset = context["subset"]["name"]
# folder_name = context["asset"]["name"]
# product_name = context["subset"]["name"]
# instance_name = prepare_scene_name(
# asset, subset, unique_number
# folder_name, product_name, unique_number
# ) + '_CON'
# return self._get_instance_collection(instance_name, nodes)

View file

@ -1,24 +1,24 @@
# -*- coding: utf-8 -*-
"""Converter for legacy Houdini subsets."""
"""Converter for legacy Houdini products."""
from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin
from ayon_core.hosts.blender.api.lib import imprint
class BlenderLegacyConvertor(SubsetConvertorPlugin):
"""Find and convert any legacy subsets in the scene.
"""Find and convert any legacy products in the scene.
This Converter will find all legacy subsets in the scene and will
transform them to the current system. Since the old subsets doesn't
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.
we can do is based on their product types.
Its limitation is that you can have multiple creators creating subset
of the same family and there is no way to handle it. This code should
nevertheless cover all creators that came with OpenPype.
Its limitation is that you can have multiple creators creating product
of the same product type and there is no way to handle it. This code
should nevertheless cover all creators that came with OpenPype.
"""
identifier = "io.openpype.creators.blender.legacy"
family_to_id = {
product_type_to_id = {
"action": "io.openpype.creators.blender.action",
"camera": "io.openpype.creators.blender.camera",
"animation": "io.openpype.creators.blender.animation",
@ -33,42 +33,42 @@ class BlenderLegacyConvertor(SubsetConvertorPlugin):
def __init__(self, *args, **kwargs):
super(BlenderLegacyConvertor, self).__init__(*args, **kwargs)
self.legacy_subsets = {}
self.legacy_instances = {}
def find_instances(self):
"""Find legacy subsets in the scene.
"""Find legacy products in the scene.
Legacy subsets are the ones that doesn't have `creator_identifier`
Legacy products are the ones that doesn't have `creator_identifier`
parameter on them.
This is using cached entries done in
:py:meth:`~BaseCreator.cache_subsets()`
:py:meth:`~BaseCreator.cache_instance_data()`
"""
self.legacy_subsets = self.collection_shared_data.get(
"blender_cached_legacy_subsets")
if not self.legacy_subsets:
self.legacy_instances = self.collection_shared_data.get(
"blender_cached_legacy_instances")
if not self.legacy_instances:
return
self.add_convertor_item(
"Found {} incompatible subset{}".format(
len(self.legacy_subsets),
"s" if len(self.legacy_subsets) > 1 else ""
"Found {} incompatible product{}".format(
len(self.legacy_instances),
"s" if len(self.legacy_instances) > 1 else ""
)
)
def convert(self):
"""Convert all legacy subsets to current.
"""Convert all legacy products to current.
It is enough to add `creator_identifier` and `instance_node`.
"""
if not self.legacy_subsets:
if not self.legacy_instances:
return
for family, instance_nodes in self.legacy_subsets.items():
if family in self.family_to_id:
for product_type, instance_nodes in self.legacy_instances.items():
if product_type in self.product_type_to_id:
for instance_node in instance_nodes:
creator_identifier = self.family_to_id[family]
creator_identifier = self.product_type_to_id[product_type]
self.log.info(
"Converting {} to {}".format(instance_node.name,
creator_identifier)

View file

@ -10,20 +10,20 @@ class CreateAction(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.action"
label = "Action"
family = "action"
product_type = "action"
icon = "male"
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
# Run parent create method
collection = super().create(
subset_name, instance_data, pre_create_data
product_name, instance_data, pre_create_data
)
# Get instance name
name = plugin.prepare_scene_name(
instance_data["folderPath"], subset_name
instance_data["folderPath"], product_name
)
if pre_create_data.get("use_selection"):

View file

@ -8,15 +8,15 @@ class CreateAnimation(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.animation"
label = "Animation"
family = "animation"
product_type = "animation"
icon = "male"
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
# Run parent create method
collection = super().create(
subset_name, instance_data, pre_create_data
product_name, instance_data, pre_create_data
)
if pre_create_data.get("use_selection"):

View file

@ -10,16 +10,16 @@ class CreateBlendScene(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.blendscene"
label = "Blender Scene"
family = "blendScene"
product_type = "blendScene"
icon = "cubes"
maintain_selection = False
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
instance_node = super().create(subset_name,
instance_node = super().create(product_name,
instance_data,
pre_create_data)

View file

@ -11,16 +11,16 @@ class CreateCamera(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.camera"
label = "Camera"
family = "camera"
product_type = "camera"
icon = "video-camera"
create_as_asset_group = True
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
asset_group = super().create(subset_name,
asset_group = super().create(product_name,
instance_data,
pre_create_data)
@ -30,8 +30,8 @@ class CreateCamera(plugin.BaseCreator):
obj.parent = asset_group
else:
plugin.deselect_all()
camera = bpy.data.cameras.new(subset_name)
camera_obj = bpy.data.objects.new(subset_name, camera)
camera = bpy.data.cameras.new(product_name)
camera_obj = bpy.data.objects.new(product_name, camera)
instances = bpy.data.collections.get(AVALON_INSTANCES)
instances.objects.link(camera_obj)

View file

@ -10,16 +10,16 @@ class CreateLayout(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.layout"
label = "Layout"
family = "layout"
product_type = "layout"
icon = "cubes"
create_as_asset_group = True
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
asset_group = super().create(subset_name,
asset_group = super().create(product_name,
instance_data,
pre_create_data)

View file

@ -10,15 +10,15 @@ class CreateModel(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.model"
label = "Model"
family = "model"
product_type = "model"
icon = "cube"
create_as_asset_group = True
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
asset_group = super().create(subset_name,
asset_group = super().create(product_name,
instance_data,
pre_create_data)

View file

@ -8,15 +8,15 @@ class CreatePointcache(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.pointcache"
label = "Point Cache"
family = "pointcache"
product_type = "pointcache"
icon = "gears"
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
# Run parent create method
collection = super().create(
subset_name, instance_data, pre_create_data
product_name, instance_data, pre_create_data
)
if pre_create_data.get("use_selection"):

View file

@ -12,16 +12,16 @@ class CreateRenderlayer(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.render"
label = "Render"
family = "render"
product_type = "render"
icon = "eye"
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
try:
# Run parent create method
collection = super().create(
subset_name, instance_data, pre_create_data
product_name, instance_data, pre_create_data
)
prepare_rendering(collection)

View file

@ -8,15 +8,15 @@ class CreateReview(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.review"
label = "Review"
family = "review"
product_type = "review"
icon = "video-camera"
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
# Run parent create method
collection = super().create(
subset_name, instance_data, pre_create_data
product_name, instance_data, pre_create_data
)
if pre_create_data.get("use_selection"):

View file

@ -10,15 +10,15 @@ class CreateRig(plugin.BaseCreator):
identifier = "io.openpype.creators.blender.rig"
label = "Rig"
family = "rig"
product_type = "rig"
icon = "wheelchair"
create_as_asset_group = True
def create(
self, subset_name: str, instance_data: dict, pre_create_data: dict
self, product_name: str, instance_data: dict, pre_create_data: dict
):
asset_group = super().create(subset_name,
asset_group = super().create(product_name,
instance_data,
pre_create_data)

View file

@ -19,7 +19,7 @@ class CreateWorkfile(BaseCreator, AutoCreator):
"""
identifier = "io.openpype.creators.blender.workfile"
label = "Workfile"
family = "workfile"
product_type = "workfile"
icon = "fa5.file"
def create(self):
@ -43,8 +43,12 @@ class CreateWorkfile(BaseCreator, AutoCreator):
if not workfile_instance:
asset_doc = get_asset_by_name(project_name, asset_name)
subset_name = self.get_subset_name(
task_name, task_name, asset_doc, project_name, host_name
product_name = self.get_product_name(
project_name,
asset_doc,
task_name,
task_name,
host_name,
)
data = {
"folderPath": asset_name,
@ -53,17 +57,17 @@ class CreateWorkfile(BaseCreator, AutoCreator):
}
data.update(
self.get_dynamic_data(
task_name,
task_name,
asset_doc,
project_name,
asset_doc,
task_name,
task_name,
host_name,
workfile_instance,
)
)
self.log.info("Auto-creating workfile instance...")
workfile_instance = CreatedInstance(
self.family, subset_name, data, self
self.product_type, product_name, data, self
)
self._add_instance_to_context(workfile_instance)
@ -73,13 +77,17 @@ class CreateWorkfile(BaseCreator, AutoCreator):
):
# Update instance context if it's different
asset_doc = get_asset_by_name(project_name, asset_name)
subset_name = self.get_subset_name(
task_name, task_name, asset_doc, project_name, host_name
product_name = self.get_product_name(
project_name,
asset_doc,
task_name,
self.default_variant,
host_name,
)
workfile_instance["folderPath"] = asset_name
workfile_instance["task"] = task_name
workfile_instance["subset"] = subset_name
workfile_instance["productName"] = product_name
instance_node = bpy.data.collections.get(AVALON_CONTAINERS)
if not instance_node:

View file

@ -4,10 +4,10 @@ from ayon_core.hosts.blender.api import plugin
def append_workfile(context, fname, do_import):
asset = context['asset']['name']
subset = context['subset']['name']
folder_name = context['asset']['name']
product_name = context['subset']['name']
group_name = plugin.prepare_scene_name(asset, subset)
group_name = plugin.prepare_scene_name(folder_name, product_name)
# We need to preserve the original names of the scenes, otherwise,
# if there are duplicate names in the current workfile, the imported

View file

@ -134,13 +134,15 @@ class CacheModelLoader(plugin.AssetLoader):
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
containers = bpy.data.collections.get(AVALON_CONTAINERS)
if not containers:
@ -159,6 +161,7 @@ class CacheModelLoader(plugin.AssetLoader):
self._link_objects(objects, asset_group, containers, asset_group)
product_type = context["subset"]["data"]["family"]
asset_group[AVALON_PROPERTY] = {
"schema": "openpype:container-2.0",
"id": AVALON_CONTAINER_ID,
@ -169,7 +172,7 @@ class CacheModelLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": product_type,
"objectName": group_name
}

View file

@ -44,11 +44,11 @@ class BlendActionLoader(plugin.AssetLoader):
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = plugin.prepare_scene_name(asset, subset)
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
lib_container = plugin.prepare_scene_name(folder_name, product_name)
container_name = plugin.prepare_scene_name(
asset, subset, namespace
folder_name, product_name, namespace
)
container = bpy.data.collections.new(lib_container)

View file

@ -39,13 +39,15 @@ class AudioLoader(plugin.AssetLoader):
options: Additional settings dictionary
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_container:
@ -85,7 +87,7 @@ class AudioLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": context["subset"]["data"]["family"],
"objectName": group_name,
"audio": audio
}

View file

@ -127,20 +127,22 @@ class BlendLoader(plugin.AssetLoader):
options: Additional settings dictionary
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
try:
family = context["representation"]["context"]["family"]
product_type = context["subset"]["data"]["family"]
except ValueError:
family = "model"
product_type = "model"
representation = str(context["representation"]["_id"])
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_container:
@ -149,8 +151,8 @@ class BlendLoader(plugin.AssetLoader):
container, members = self._process_data(libpath, group_name)
if family == "layout":
self._post_process_layout(container, asset, representation)
if product_type == "layout":
self._post_process_layout(container, folder_name, representation)
avalon_container.objects.link(container)
@ -164,7 +166,7 @@ class BlendLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": context["subset"]["data"]["family"],
"objectName": group_name,
"members": members,
}

View file

@ -34,7 +34,7 @@ class BlendSceneLoader(plugin.AssetLoader):
return None
def _process_data(self, libpath, group_name, family):
def _process_data(self, libpath, group_name, product_type):
# Append all the data from the .blend file
with bpy.data.libraries.load(
libpath, link=False, relative=False
@ -82,25 +82,29 @@ class BlendSceneLoader(plugin.AssetLoader):
options: Additional settings dictionary
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
try:
family = context["representation"]["context"]["family"]
product_type = context["subset"]["data"]["family"]
except ValueError:
family = "model"
product_type = "model"
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_container:
avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS)
bpy.context.scene.collection.children.link(avalon_container)
container, members = self._process_data(libpath, group_name, family)
container, members = self._process_data(
libpath, group_name, product_type
)
avalon_container.children.link(container)
@ -114,7 +118,7 @@ class BlendSceneLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": context["subset"]["data"]["family"],
"objectName": group_name,
"members": members,
}
@ -167,8 +171,12 @@ class BlendSceneLoader(plugin.AssetLoader):
self.exec_remove(container)
family = container["family"]
asset_group, members = self._process_data(libpath, group_name, family)
product_type = container.get("productType")
if product_type is None:
product_type = container["family"]
asset_group, members = self._process_data(
libpath, group_name, product_type
)
for member in members:
if member.name in collection_parents:

View file

@ -84,13 +84,15 @@ class AbcCameraLoader(plugin.AssetLoader):
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_container:
@ -121,7 +123,7 @@ class AbcCameraLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": context["subset"]["data"]["family"],
"objectName": group_name,
}

View file

@ -87,13 +87,15 @@ class FbxCameraLoader(plugin.AssetLoader):
options: Additional settings dictionary
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_container:
@ -124,7 +126,7 @@ class FbxCameraLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": context["subset"]["data"]["family"],
"objectName": group_name
}

View file

@ -131,13 +131,15 @@ class FbxModelLoader(plugin.AssetLoader):
options: Additional settings dictionary
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_container:
@ -168,7 +170,7 @@ class FbxModelLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": context["subset"]["data"]["family"],
"objectName": group_name
}

View file

@ -50,11 +50,11 @@ class JsonLayoutLoader(plugin.AssetLoader):
if anim_collection:
bpy.data.collections.remove(anim_collection)
def _get_loader(self, loaders, family):
def _get_loader(self, loaders, product_type):
name = ""
if family == 'rig':
if product_type == 'rig':
name = "BlendRigLoader"
elif family == 'model':
elif product_type == 'model':
name = "BlendModelLoader"
if name == "":
@ -76,10 +76,12 @@ class JsonLayoutLoader(plugin.AssetLoader):
for element in data:
reference = element.get('reference')
family = element.get('family')
product_type = element.get("product_type")
if product_type is None:
product_type = element.get("family")
loaders = loaders_from_representation(all_loaders, reference)
loader = self._get_loader(loaders, family)
loader = self._get_loader(loaders, product_type)
if not loader:
continue
@ -95,7 +97,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
'parent': asset_group,
'transform': element.get('transform'),
'action': action,
'create_animation': True if family == 'rig' else False,
'create_animation': True if product_type == 'rig' else False,
'animation_asset': asset
}
@ -127,7 +129,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
# legacy_create(
# creator_plugin,
# name="camera",
# # name=f"{unique_number}_{subset}_animation",
# # name=f"{unique_number}_{product[name]}_animation",
# asset=asset,
# options={"useSelection": False}
# # data={"dependencies": str(context["representation"]["_id"])}
@ -146,13 +148,15 @@ class JsonLayoutLoader(plugin.AssetLoader):
options: Additional settings dictionary
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
asset_name = plugin.prepare_scene_name(asset, subset)
unique_number = plugin.get_unique_number(asset, subset)
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
asset_name = plugin.prepare_scene_name(folder_name, product_name)
unique_number = plugin.get_unique_number(folder_name, product_name)
group_name = plugin.prepare_scene_name(
folder_name, product_name, unique_number
)
namespace = namespace or f"{folder_name}_{unique_number}"
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_container:
@ -177,7 +181,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
"libpath": libpath,
"asset_name": asset_name,
"parent": str(context["representation"]["parent"]),
"family": context["representation"]["context"]["family"],
"productType": context["subset"]["data"]["family"],
"objectName": group_name
}
@ -239,7 +243,10 @@ class JsonLayoutLoader(plugin.AssetLoader):
for obj in asset_group.children:
obj_meta = obj.get(AVALON_PROPERTY)
if obj_meta.get('family') == 'rig':
product_type = obj_meta.get("productType")
if product_type is None:
product_type = obj_meta.get("family")
if product_type == "rig":
rig = None
for child in obj.children:
if child.type == 'ARMATURE':

View file

@ -93,18 +93,18 @@ class BlendLookLoader(plugin.AssetLoader):
"""
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
folder_name = context["asset"]["name"]
product_name = context["subset"]["name"]
lib_container = plugin.prepare_scene_name(
asset, subset
folder_name, product_name
)
unique_number = plugin.get_unique_number(
asset, subset
folder_name, product_name
)
namespace = namespace or f"{asset}_{unique_number}"
namespace = namespace or f"{folder_name}_{unique_number}"
container_name = plugin.prepare_scene_name(
asset, subset, unique_number
folder_name, product_name, unique_number
)
container = bpy.data.collections.new(lib_container)
@ -131,7 +131,7 @@ class BlendLookLoader(plugin.AssetLoader):
metadata["materials"] = materials
metadata["parent"] = str(context["representation"]["parent"])
metadata["family"] = context["representation"]["context"]["family"]
metadata["product_type"] = context["subset"]["data"]["family"]
nodes = list(container.objects)
nodes.append(container)

View file

@ -25,7 +25,7 @@ class CollectBlenderInstanceData(pyblish.api.InstancePlugin):
members.extend(instance_node.children)
# Special case for animation instances, include armatures
if instance.data["family"] == "animation":
if instance.data["productType"] == "animation":
for obj in instance_node.objects:
if obj.type == 'EMPTY' and obj.get(AVALON_PROPERTY):
members.extend(

View file

@ -19,9 +19,9 @@ class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
# Define extract output file path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.abc"
filepath = os.path.join(stagingdir, filename)

View file

@ -23,9 +23,9 @@ class ExtractAnimationABC(
# Define extract output file path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.abc"
filepath = os.path.join(stagingdir, filename)

View file

@ -20,9 +20,9 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
# Define extract output file path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.blend"
filepath = os.path.join(stagingdir, filename)

View file

@ -23,9 +23,9 @@ class ExtractBlendAnimation(
# Define extract output file path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.blend"
filepath = os.path.join(stagingdir, filename)

View file

@ -21,9 +21,9 @@ class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
# Define extract output file path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.abc"
filepath = os.path.join(stagingdir, filename)

View file

@ -20,9 +20,9 @@ class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin):
# Define extract output file path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.fbx"
filepath = os.path.join(stagingdir, filename)

View file

@ -21,9 +21,9 @@ class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin):
# Define extract output file path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.fbx"
filepath = os.path.join(stagingdir, filename)

View file

@ -145,9 +145,9 @@ class ExtractAnimationFBX(
root.select_set(True)
armature.select_set(True)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
fbx_filename = f"{instance_name}_{armature.name}.fbx"
filepath = os.path.join(stagingdir, fbx_filename)

View file

@ -133,7 +133,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
fbx_count = 0
project_name = instance.context.data["projectEntity"]["name"]
project_name = instance.context.data["projectName"]
for asset in asset_group.children:
metadata = asset.get(AVALON_PROPERTY)
if not metadata:
@ -147,7 +147,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
continue
version_id = metadata["parent"]
family = metadata["family"]
product_type = metadata.get("product_type")
if product_type is None:
product_type = metadata["family"]
self.log.debug("Parent: {}".format(version_id))
# Get blend reference
@ -179,7 +181,8 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
json_element["reference_fbx"] = str(fbx_id)
if abc_id:
json_element["reference_abc"] = str(abc_id)
json_element["family"] = family
json_element["product_type"] = product_type
json_element["instance_name"] = asset.name
json_element["asset_name"] = metadata["asset_name"]
json_element["file_path"] = metadata["libpath"]
@ -215,7 +218,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
]
# Extract the animation as well
if family == "rig":
if product_type == "rig":
f, n = self._export_animation(
asset, instance, stagingdir, fbx_count)
if f:
@ -225,9 +228,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
json_data.append(json_element)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
instance_name = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
instance_name = f"{folder_name}_{product_name}"
json_filename = f"{instance_name}.json"
json_path = os.path.join(stagingdir, json_filename)

View file

@ -55,9 +55,9 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
# get output path
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
filename = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
filename = f"{folder_name}_{product_name}"
path = os.path.join(stagingdir, filename)

View file

@ -32,9 +32,9 @@ class ExtractThumbnail(publish.Extractor):
return
stagingdir = self.staging_dir(instance)
asset_name = instance.data["assetEntity"]["name"]
subset = instance.data["subset"]
filename = f"{asset_name}_{subset}"
folder_name = instance.data["assetEntity"]["name"]
product_name = instance.data["productName"]
filename = f"{folder_name}_{product_name}"
path = os.path.join(stagingdir, filename)
@ -42,11 +42,11 @@ class ExtractThumbnail(publish.Extractor):
camera = instance.data.get("review_camera", "AUTO")
start = instance.data.get("frameStart", bpy.context.scene.frame_start)
family = instance.data.get("family")
product_type = instance.data["productType"]
isolate = instance.data("isolate", None)
presets = json.loads(self.presets)
preset = presets.get(family, {})
preset = presets.get(product_type, {})
preset.update({
"camera": camera,

View file

@ -28,25 +28,26 @@ class IntegrateAnimation(
# Update the json file for the setdress to add the published
# representations of the animations
for json_dict in data:
json_product_name = json_dict["productName"]
i = None
for elem in instance.context:
if elem.data.get('subset') == json_dict['subset']:
if elem.data["productName"] == json_product_name:
i = elem
break
if not i:
continue
rep = None
pub_repr = i.data.get('published_representations')
pub_repr = i.data["published_representations"]
for elem in pub_repr:
if pub_repr.get(elem).get('representation').get('name') == "fbx":
rep = pub_repr.get(elem)
if pub_repr[elem]["representation"]["name"] == "fbx":
rep = pub_repr[elem]
break
if not rep:
continue
obj_id = rep.get('representation').get('_id')
obj_id = rep["representation"]["_id"]
if obj_id:
json_dict['_id'] = str(obj_id)
json_dict["representation_id"] = str(obj_id)
with open(json_path, "w") as file:
json.dump(data, fp=file, indent=2)

View file

@ -26,6 +26,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
hosts = ["blender"]
label = "Validate File Saved"
optional = False
# TODO rename to 'exclude_product_types'
exclude_families = []
actions = [SaveWorkfileAction]
@ -41,8 +42,8 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
# Do not validate workfile has unsaved changes if only instances
# present of families that should be excluded
families = {
instance.data["family"] for instance in context
product_types = {
instance.data["productType"] for instance in context
# Consider only enabled instances
if instance.data.get("publish", True)
and instance.data.get("active", True)
@ -52,7 +53,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
return any(family in exclude_family
for exclude_family in self.exclude_families)
if all(is_excluded(family) for family in families):
if all(is_excluded(product_type) for product_type in product_types):
self.log.debug("Only excluded families found, skipping workfile "
"unsaved changes validation..")
return

View file

@ -46,17 +46,18 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
shared_instance_data.update(celaction_kwargs)
# workfile instance
family = "workfile"
subset = family + task.capitalize()
product_type = "workfile"
product_name = product_type + task.capitalize()
# Create instance
instance = context.create_instance(subset)
instance = context.create_instance(product_name)
# creating instance data
instance.data.update({
"subset": subset,
"label": scene_file,
"family": family,
"families": [],
"productName": product_name,
"productType": product_type,
"family": product_type,
"families": [product_type],
"representations": []
})
@ -76,17 +77,19 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
self.log.info('Publishing Celaction workfile')
# render instance
subset = f"render{task}Main"
instance = context.create_instance(name=subset)
product_name = f"render{task}Main"
product_type = "render.farm"
instance = context.create_instance(name=product_name)
# getting instance state
instance.data["publish"] = True
# add assetEntity data into instance
instance.data.update({
"label": "{} - farm".format(subset),
"family": "render.farm",
"families": [],
"subset": subset
"label": "{} - farm".format(product_name),
"productType": product_type,
"family": product_type,
"families": [product_type],
"productName": product_name
})
# adding basic script data

View file

@ -19,12 +19,14 @@ class CollectRenderPath(pyblish.api.InstancePlugin):
anatomy = instance.context.data["anatomy"]
anatomy_data = copy.deepcopy(instance.data["anatomyData"])
padding = anatomy.templates.get("frame_padding", 4)
product_type = "render"
anatomy_data.update({
"frame": f"%0{padding}d",
"family": "render",
"family": product_type,
"representation": self.output_extension,
"ext": self.output_extension
})
anatomy_data["product"]["type"] = product_type
anatomy_filled = anatomy.format(anatomy_data)

View file

@ -147,8 +147,8 @@ def imprint(segment, data=None):
Examples:
data = {
'asset': 'sq020sh0280',
'family': 'render',
'subset': 'subsetMain'
'productType': 'render',
'productName': 'productMain'
}
"""
data = data or {}

View file

@ -353,9 +353,9 @@ class PublishableClip:
rename_default = False
hierarchy_default = "{_folder_}/{_sequence_}/{_track_}"
clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}"
subset_name_default = "[ track name ]"
review_track_default = "[ none ]"
subset_family_default = "plate"
base_product_name_default = "[ track name ]"
base_product_type_default = "plate"
count_from_default = 10
count_steps_default = 10
vertical_sync_default = False
@ -368,7 +368,7 @@ class PublishableClip:
def __init__(self, segment, **kwargs):
self.rename_index = kwargs["rename_index"]
self.family = kwargs["family"]
self.product_type = kwargs["family"]
self.log = kwargs["log"]
# get main parent objects
@ -486,10 +486,10 @@ class PublishableClip:
"countFrom", {}).get("value") or self.count_from_default
self.count_steps = self.ui_inputs.get(
"countSteps", {}).get("value") or self.count_steps_default
self.subset_name = self.ui_inputs.get(
"subsetName", {}).get("value") or self.subset_name_default
self.subset_family = self.ui_inputs.get(
"subsetFamily", {}).get("value") or self.subset_family_default
self.base_product_name = self.ui_inputs.get(
"productName", {}).get("value") or self.base_product_name_default
self.base_product_type = self.ui_inputs.get(
"productType", {}).get("value") or self.base_product_type_default
self.vertical_sync = self.ui_inputs.get(
"vSyncOn", {}).get("value") or self.vertical_sync_default
self.driving_layer = self.ui_inputs.get(
@ -509,12 +509,14 @@ class PublishableClip:
or self.retimed_framerange_default
)
# build subset name from layer name
if self.subset_name == "[ track name ]":
self.subset_name = self.track_name
# build product name from layer name
if self.base_product_name == "[ track name ]":
self.base_product_name = self.track_name
# create subset for publishing
self.subset = self.subset_family + self.subset_name.capitalize()
# create product for publishing
self.product_name = (
self.base_product_type + self.base_product_name.capitalize()
)
def _replace_hash_to_expression(self, name, text):
""" Replace hash with number in correct padding. """
@ -608,14 +610,14 @@ class PublishableClip:
_hero_data = deepcopy(hero_data)
_hero_data.update({"heroTrack": False})
if _in <= self.clip_in and _out >= self.clip_out:
data_subset = hero_data["subset"]
data_product_name = hero_data["productName"]
# add track index in case duplicity of names in hero data
if self.subset in data_subset:
_hero_data["subset"] = self.subset + str(
if self.product_name in data_product_name:
_hero_data["productName"] = self.product_name + str(
self.track_index)
# in case track name and subset name is the same then add
if self.subset_name == self.track_name:
_hero_data["subset"] = self.subset
# in case track name and product name is the same then add
if self.base_product_name == self.track_name:
_hero_data["productName"] = self.product_name
# assign data to return hierarchy data to tag
tag_hierarchy_data = _hero_data
break
@ -637,9 +639,9 @@ class PublishableClip:
"hierarchy": hierarchy_filled,
"parents": self.parents,
"hierarchyData": hierarchy_formatting_data,
"subset": self.subset,
"family": self.subset_family,
"families": [self.family]
"productName": self.product_name,
"productType": self.base_product_type,
"families": [self.base_product_type, self.product_type]
}
def _convert_to_entity(self, type, template):

View file

@ -6,7 +6,7 @@ class CreateShotClip(opfapi.Creator):
"""Publishable clip"""
label = "Create Publishable Clip"
family = "clip"
product_type = "clip"
icon = "film"
defaults = ["Main"]
@ -32,7 +32,7 @@ class CreateShotClip(opfapi.Creator):
# open widget for plugins inputs
results_back = self.create_widget(
"Pype publish attributes creator",
"AYON publish attributes creator",
"Define sequential rename and fill hierarchy data.",
gui_inputs
)
@ -62,7 +62,7 @@ class CreateShotClip(opfapi.Creator):
"log": self.log,
"ui_inputs": results_back,
"avalon": self.data,
"family": self.data["family"]
"product_type": self.data["productType"]
}
for i, segment in enumerate(sorted_selected_segments):
@ -203,19 +203,19 @@ class CreateShotClip(opfapi.Creator):
"target": "ui",
"order": 3,
"value": {
"subsetName": {
"productName": {
"value": ["[ track name ]", "main", "bg", "fg", "bg",
"animatic"],
"type": "QComboBox",
"label": "Subset Name",
"target": "ui",
"toolTip": "chose subset name pattern, if [ track name ] is selected, name of track layer will be used", # noqa
"toolTip": "chose product name pattern, if [ track name ] is selected, name of track layer will be used", # noqa
"order": 0},
"subsetFamily": {
"productType": {
"value": ["plate", "take"],
"type": "QComboBox",
"label": "Subset Family",
"target": "ui", "toolTip": "What use of this subset is for", # noqa
"target": "ui", "toolTip": "What use of this product is for", # noqa
"order": 1},
"reviewTrack": {
"value": ["< none >"] + gui_tracks,
@ -229,7 +229,7 @@ class CreateShotClip(opfapi.Creator):
"type": "QCheckBox",
"label": "Include audio",
"target": "tag",
"toolTip": "Process subsets with corresponding audio", # noqa
"toolTip": "Process products with corresponding audio", # noqa
"order": 3},
"sourceResolution": {
"value": False,

View file

@ -11,7 +11,7 @@ from ayon_core.lib.transcoding import (
class LoadClip(opfapi.ClipLoader):
"""Load a subset to timeline as clip
"""Load a product to timeline as clip
Place clip to timeline on its asset origin timings collected
during conforming to project
@ -31,14 +31,14 @@ class LoadClip(opfapi.ClipLoader):
# settings
reel_group_name = "OpenPype_Reels"
reel_name = "Loaded"
clip_name_template = "{asset}_{subset}<_{output}>"
clip_name_template = "{folder[name]}_{product[name]}<_{output}>"
""" Anatomy keys from version context data and dynamically added:
- {layerName} - original layer name token
- {layerUID} - original layer UID token
- {originalBasename} - original clip name taken from file
"""
layer_rename_template = "{asset}_{subset}<_{output}>"
layer_rename_template = "{folder[name]}_{product[name]}<_{output}>"
layer_rename_patterns = []
def load(self, context, name, namespace, options):

View file

@ -10,7 +10,7 @@ from ayon_core.lib.transcoding import (
)
class LoadClipBatch(opfapi.ClipLoader):
"""Load a subset to timeline as clip
"""Load a product to timeline as clip
Place clip to timeline on its asset origin timings collected
during conforming to project
@ -29,14 +29,14 @@ class LoadClipBatch(opfapi.ClipLoader):
# settings
reel_name = "OP_LoadedReel"
clip_name_template = "{batch}_{asset}_{subset}<_{output}>"
clip_name_template = "{batch}_{folder[name]}_{product[name]}<_{output}>"
""" Anatomy keys from version context data and dynamically added:
- {layerName} - original layer name token
- {layerUID} - original layer UID token
- {originalBasename} - original clip name taken from file
"""
layer_rename_template = "{asset}_{subset}<_{output}>"
layer_rename_template = "{folder[name]}_{product[name]}<_{output}>"
layer_rename_patterns = []
def load(self, context, name, namespace, options):
@ -50,17 +50,8 @@ class LoadClipBatch(opfapi.ClipLoader):
version_name = version.get("name", None)
colorspace = self.get_colorspace(context)
# TODO remove '{folder[name]}' and '{product[name]}' replacement
clip_name_template = (
self.clip_name_template
.replace("{folder[name]}", "{asset}")
.replace("{product[name]}", "{subset}")
)
layer_rename_template = (
self.layer_rename_template
.replace("{folder[name]}", "{asset}")
.replace("{product[name]}", "{subset}")
)
clip_name_template = self.clip_name_template
layer_rename_template = self.layer_rename_template
# in case output is not in context replace key to representation
if not context["representation"]["context"].get("output"):
clip_name_template = clip_name_template.replace(
@ -68,8 +59,22 @@ class LoadClipBatch(opfapi.ClipLoader):
layer_rename_template = layer_rename_template.replace(
"output", "representation")
asset_doc = context["asset"]
subset_doc = context["subset"]
formatting_data = deepcopy(context["representation"]["context"])
formatting_data["batch"] = self.batch.name.get_value()
formatting_data.update({
"asset": asset_doc["name"],
"folder": {
"name": asset_doc["name"],
},
"subset": subset_doc["name"],
"family": subset_doc["data"]["family"],
"product": {
"name": subset_doc["name"],
"type": subset_doc["data"]["family"],
}
})
clip_name = StringTemplate(clip_name_template).format(
formatting_data)

View file

@ -59,6 +59,6 @@ class CollectTestSelection(pyblish.api.ContextPlugin):
opfapi.imprint(segment, {
'asset': segment.name.get_value(),
'family': 'render',
'subset': 'subsetMain'
'productType': 'render',
'productName': 'productMain'
})

View file

@ -110,24 +110,25 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
# add ocio_data to instance data
inst_data.update(otio_data)
asset = marker_data["asset"]
subset = marker_data["subset"]
folder_path = marker_data["folderPath"]
folder_name = folder_path.rsplit("/")[-1]
product_name = marker_data["productName"]
# insert family into families
family = marker_data["family"]
# insert product type into families
product_type = marker_data["productType"]
families = [str(f) for f in marker_data["families"]]
families.insert(0, str(family))
families.insert(0, str(product_type))
# form label
label = asset
if asset != clip_name:
label = folder_name
if folder_name != clip_name:
label += " ({})".format(clip_name)
label += " {} [{}]".format(subset, ", ".join(families))
label += " {} [{}]".format(product_name, ", ".join(families))
inst_data.update({
"name": "{}_{}".format(asset, subset),
"name": "{}_{}".format(folder_name, product_name),
"label": label,
"asset": asset,
"folderPath": folder_path,
"item": segment,
"families": families,
"publish": marker_data["publish"],
@ -335,26 +336,28 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
if not hierarchy_data:
return
asset = data["asset"]
subset = "shotMain"
folder_path = data["folderPath"]
folder_name = folder_path.rsplit("/")[-1]
product_name = "shotMain"
# insert family into families
family = "shot"
# insert product type into families
product_type = "shot"
# form label
label = asset
if asset != clip_name:
label = folder_name
if folder_name != clip_name:
label += " ({}) ".format(clip_name)
label += " {}".format(subset)
label += " [{}]".format(family)
label += " {}".format(product_name)
label += " [{}]".format(product_type)
data.update({
"name": "{}_{}".format(asset, subset),
"name": "{}_{}".format(folder_name, product_name),
"label": label,
"subset": subset,
"asset": asset,
"family": family,
"families": []
"productName": product_name,
"folderPath": folder_path,
"productType": product_type,
"family": product_type,
"families": [product_type]
})
instance = context.create_instance(**data)

View file

@ -3,7 +3,7 @@ import pyblish.api
from ayon_core.client import get_asset_name_identifier
import ayon_core.hosts.flame.api as opfapi
from ayon_core.hosts.flame.otio import flame_export
from ayon_core.pipeline.create import get_subset_name
from ayon_core.pipeline.create import get_product_name
class CollecTimelineOTIO(pyblish.api.ContextPlugin):
@ -14,7 +14,7 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
def process(self, context):
# plugin defined
family = "workfile"
product_type = "workfile"
variant = "otioTimeline"
# main
@ -23,14 +23,14 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
project = opfapi.get_current_project()
sequence = opfapi.get_current_sequence(opfapi.CTX.selection)
# create subset name
subset_name = get_subset_name(
family,
variant,
task_name,
asset_doc,
# create product name
product_name = get_product_name(
context.data["projectName"],
asset_doc,
task_name,
context.data["hostName"],
product_type,
variant,
project_settings=context.data["project_settings"]
)
@ -41,11 +41,12 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
otio_timeline = flame_export.create_otio_timeline(sequence)
instance_data = {
"name": subset_name,
"name": product_name,
"folderPath": folder_path,
"subset": subset_name,
"family": "workfile",
"families": []
"productName": product_name,
"productType": product_type,
"family": product_type,
"families": [product_type]
}
# create instance with workfile

View file

@ -44,8 +44,8 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin):
))
# load plate to batch group
self.log.info("Loading subset `{}` into batch `{}`".format(
instance.data["subset"], bgroup.name.get_value()
self.log.info("Loading product `{}` into batch `{}`".format(
instance.data["productName"], bgroup.name.get_value()
))
self._load_clip_to_context(instance, bgroup)

View file

@ -33,14 +33,16 @@ class GenericCreateSaver(Creator):
# TODO: This should be renamed together with Nuke so it is aligned
temp_rendering_path_template = (
"{workdir}/renders/fusion/{subset}/{subset}.{frame}.{ext}")
"{workdir}/renders/fusion/{product[name]}/"
"{product[name]}.{frame}.{ext}"
)
def create(self, subset_name, instance_data, pre_create_data):
def create(self, product_name, instance_data, pre_create_data):
self.pass_pre_attributes_to_instance(instance_data, pre_create_data)
instance = CreatedInstance(
family=self.family,
subset_name=subset_name,
product_type=self.product_type,
product_name=product_name,
data=instance_data,
creator=self,
)
@ -111,23 +113,23 @@ class GenericCreateSaver(Creator):
tool.SetData(f"openpype.{key}", value)
def _update_tool_with_data(self, tool, data):
"""Update tool node name and output path based on subset data"""
if "subset" not in data:
"""Update tool node name and output path based on product data"""
if "productName" not in data:
return
original_subset = tool.GetData("openpype.subset")
original_product_name = tool.GetData("openpype.productName")
original_format = tool.GetData(
"openpype.creator_attributes.image_format"
)
subset = data["subset"]
product_name = data["productName"]
if (
original_subset != subset
original_product_name != product_name
or original_format != data["creator_attributes"]["image_format"]
):
self._configure_saver_tool(data, tool, subset)
self._configure_saver_tool(data, tool, product_name)
def _configure_saver_tool(self, data, tool, subset):
def _configure_saver_tool(self, data, tool, product_name):
formatting_data = deepcopy(data)
# get frame padding from anatomy templates
@ -137,25 +139,39 @@ class GenericCreateSaver(Creator):
ext = data["creator_attributes"]["image_format"]
# Subset change detected
product_type = formatting_data["productType"]
f_product_name = formatting_data["productName"]
folder_path = formatting_data["folderPath"]
folder_name = folder_path.rsplit("/", 1)[-1]
workdir = os.path.normpath(os.getenv("AYON_WORKDIR"))
formatting_data.update({
"workdir": workdir,
"frame": "0" * frame_padding,
"ext": ext,
"product": {
"name": formatting_data["subset"],
"type": formatting_data["family"],
"name": f_product_name,
"type": product_type,
},
# TODO add more variants for 'folder' and 'task'
"folder": {
"name": folder_name,
},
"task": {
"name": data["task"],
},
# Backwards compatibility
"asset": folder_name,
"subset": f_product_name,
"family": product_type,
})
# build file path to render
# TODO make sure the keys are available in 'formatting_data'
temp_rendering_path_template = (
self.temp_rendering_path_template
.replace("{product[name]}", "{subset}")
.replace("{product[type]}", "{family}")
.replace("{folder[name]}", "{asset}")
.replace("{task[name]}", "{task}")
.replace("{task}", "{task[name]}")
)
filepath = temp_rendering_path_template.format(**formatting_data)
@ -164,9 +180,9 @@ class GenericCreateSaver(Creator):
tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath))
# Rename tool
if tool.Name != subset:
print(f"Renaming {tool.Name} -> {subset}")
tool.SetAttrs({"TOOLS_Name": subset})
if tool.Name != product_name:
print(f"Renaming {tool.Name} -> {product_name}")
tool.SetAttrs({"TOOLS_Name": product_name})
def get_managed_tool_data(self, tool):
"""Return data of the tool if it matches creator identifier"""

View file

@ -17,7 +17,7 @@ class CreateImageSaver(GenericCreateSaver):
identifier = "io.openpype.creators.fusion.imagesaver"
label = "Image (saver)"
name = "image"
family = "image"
product_type = "image"
description = "Fusion Saver to generate image"
default_frame = 0

View file

@ -12,7 +12,7 @@ class CreateSaver(GenericCreateSaver):
identifier = "io.openpype.creators.fusion.saver"
label = "Render (saver)"
name = "render"
family = "render"
product_type = "render"
description = "Fusion Saver to generate image sequence"
default_frame_range_option = "asset_db"

View file

@ -10,7 +10,7 @@ from ayon_core.pipeline import (
class FusionWorkfileCreator(AutoCreator):
identifier = "workfile"
family = "workfile"
product_type = "workfile"
label = "Workfile"
icon = "fa5.file"
@ -27,9 +27,12 @@ class FusionWorkfileCreator(AutoCreator):
if not data:
return
product_name = data.get("productName")
if product_name is None:
product_name = data["subset"]
instance = CreatedInstance(
family=self.family,
subset_name=data["subset"],
product_type=self.product_type,
product_name=product_name,
data=data,
creator=self
)
@ -59,7 +62,7 @@ class FusionWorkfileCreator(AutoCreator):
existing_instance = None
for instance in self.create_context.instances:
if instance.family == self.family:
if instance.product_type == self.product_type:
existing_instance = instance
break
@ -75,9 +78,12 @@ class FusionWorkfileCreator(AutoCreator):
if existing_instance is None:
asset_doc = get_asset_by_name(project_name, asset_name)
subset_name = self.get_subset_name(
self.default_variant, task_name, asset_doc,
project_name, host_name
product_name = self.get_product_name(
project_name,
asset_doc,
task_name,
self.default_variant,
host_name,
)
data = {
"folderPath": asset_name,
@ -85,12 +91,17 @@ class FusionWorkfileCreator(AutoCreator):
"variant": self.default_variant,
}
data.update(self.get_dynamic_data(
self.default_variant, task_name, asset_doc,
project_name, host_name, None
project_name,
asset_doc,
task_name,
self.default_variant,
host_name,
None
))
new_instance = CreatedInstance(
self.family, subset_name, data, self
self.product_type, product_name, data, self
)
new_instance.transient_data["comp"] = comp
self._add_instance_to_context(new_instance)
@ -100,10 +111,13 @@ class FusionWorkfileCreator(AutoCreator):
or existing_instance["task"] != task_name
):
asset_doc = get_asset_by_name(project_name, asset_name)
subset_name = self.get_subset_name(
self.default_variant, task_name, asset_doc,
project_name, host_name
product_name = self.get_product_name(
project_name,
asset_doc,
task_name,
self.default_variant,
host_name,
)
existing_instance["folderPath"] = asset_name
existing_instance["task"] = task_name
existing_instance["subset"] = subset_name
existing_instance["productName"] = product_name

View file

@ -26,7 +26,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
instance.data["frame_range_source"] = frame_range_source
# get asset frame ranges to all instances
# render family instances `asset_db` render target
# render product type instances `asset_db` render target
start = context.data["frameStart"]
end = context.data["frameEnd"]
handle_start = context.data["handleStart"]
@ -34,7 +34,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
start_with_handle = start - handle_start
end_with_handle = end + handle_end
# conditions for render family instances
# conditions for render product type instances
if frame_range_source == "render_range":
# set comp render frame ranges
start = context.data["renderFrameStart"]
@ -70,11 +70,11 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
end_with_handle = frame
# Include start and end render frame in label
subset = instance.data["subset"]
product_name = instance.data["productName"]
label = (
"{subset} ({start}-{end}) [{handle_start}-{handle_end}]"
"{product_name} ({start}-{end}) [{handle_start}-{handle_end}]"
).format(
subset=subset,
product_name=product_name,
start=int(start),
end=int(end),
handle_start=int(handle_start),

View file

@ -49,31 +49,32 @@ class CollectFusionRender(
if not inst.data.get("active", True):
continue
family = inst.data["family"]
if family not in ["render", "image"]:
product_type = inst.data["productType"]
if product_type not in ["render", "image"]:
continue
task_name = context.data["task"]
tool = inst.data["transientData"]["tool"]
instance_families = inst.data.get("families", [])
subset_name = inst.data["subset"]
product_name = inst.data["productName"]
instance = FusionRenderInstance(
family=family,
tool=tool,
workfileComp=comp,
productType=product_type,
family=product_type,
families=instance_families,
version=version,
time="",
source=current_file,
label=inst.data["label"],
subset=subset_name,
productName=product_name,
folderPath=inst.data["folderPath"],
task=task_name,
attachTo=False,
setMembers='',
publish=True,
name=subset_name,
name=product_name,
resolutionWidth=comp_frame_format_prefs.get("Width"),
resolutionHeight=comp_frame_format_prefs.get("Height"),
pixelAspect=aspect_x / aspect_y,

View file

@ -7,7 +7,7 @@ from ayon_core.hosts.fusion.api.action import SelectInvalidAction
class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
"""Ensure all instances have a unique subset name"""
"""Ensure all instances have a unique product name"""
order = pyblish.api.ValidatorOrder
label = "Validate Unique Subsets"
@ -18,27 +18,31 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
@classmethod
def get_invalid(cls, context):
# Collect instances per subset per asset
instances_per_subset_asset = defaultdict(lambda: defaultdict(list))
# Collect instances per product per folder
instances_per_product_folder = defaultdict(lambda: defaultdict(list))
for instance in context:
asset = instance.data.get(
"folderPath", context.data.get("folderPath")
folder_path = instance.data["folderPath"]
product_name = instance.data["productName"]
instances_per_product_folder[folder_path][product_name].append(
instance
)
subset = instance.data.get("subset", context.data.get("subset"))
instances_per_subset_asset[asset][subset].append(instance)
# Find which asset + subset combination has more than one instance
# Those are considered invalid because they'd integrate to the same
# destination.
invalid = []
for asset, instances_per_subset in instances_per_subset_asset.items():
for subset, instances in instances_per_subset.items():
for folder_path, instances_per_product in (
instances_per_product_folder.items()
):
for product_name, instances in instances_per_product.items():
if len(instances) > 1:
cls.log.warning(
"{asset} > {subset} used by more than "
"one instance: {instances}".format(
asset=asset,
subset=subset,
(
"{folder_path} > {product_name} used by more than "
"one instance: {instances}"
).format(
folder_path=folder_path,
product_name=product_name,
instances=instances
)
)
@ -52,6 +56,7 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
def process(self, context):
invalid = self.get_invalid(context)
if invalid:
raise PublishValidationError("Multiple instances are set to "
"the same asset > subset.",
title=self.label)
raise PublishValidationError(
"Multiple instances are set to the same folder > product.",
title=self.label
)

View file

@ -204,7 +204,7 @@ class CreateComposite(harmony.Creator):
name = "compositeDefault"
label = "Composite"
family = "mindbender.template"
product_type = "mindbender.template"
def __init__(self, *args, **kwargs):
super(CreateComposite, self).__init__(*args, **kwargs)
@ -221,7 +221,7 @@ class CreateRender(harmony.Creator):
name = "writeDefault"
label = "Write"
family = "mindbender.imagesequence"
product_type = "mindbender.imagesequence"
node_type = "WRITE"
def __init__(self, *args, **kwargs):

View file

@ -88,7 +88,7 @@ ImageSequenceLoader.getUniqueColumnName = function(columnPrefix) {
* var args = [
* files, // Files in file sequences.
* asset, // Asset name.
* subset, // Subset name.
* productName, // Product name.
* startFrame, // Sequence starting frame.
* groupId // Unique group ID (uuid4).
* ];
@ -106,7 +106,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) {
var doc = $.scn;
var files = args[0];
var asset = args[1];
var subset = args[2];
var productName = args[2];
var startFrame = args[3];
var groupId = args[4];
var vectorFormat = null;
@ -124,7 +124,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) {
var num = 0;
var name = '';
do {
name = asset + '_' + (num++) + '_' + subset;
name = asset + '_' + (num++) + '_' + productName;
} while (currentGroup.getNodeByName(name) != null);
extension = filename.substr(pos+1).toLowerCase();

View file

@ -31,7 +31,7 @@ var TemplateLoader = function() {};
* var args = [
* templatePath, // Path to tpl file.
* assetName, // Asset name.
* subsetName, // Subset name.
* productName, // Product name.
* groupId // unique ID (uuid4)
* ];
*/
@ -39,7 +39,7 @@ TemplateLoader.prototype.loadContainer = function(args) {
var doc = $.scn;
var templatePath = args[0];
var assetName = args[1];
var subset = args[2];
var productName = args[2];
var groupId = args[3];
// Get the current group
@ -62,7 +62,7 @@ TemplateLoader.prototype.loadContainer = function(args) {
var num = 0;
var containerGroupName = '';
do {
containerGroupName = assetName + '_' + (num++) + '_' + subset;
containerGroupName = assetName + '_' + (num++) + '_' + productName;
} while (currentGroup.getNodeByName(containerGroupName) != null);
// import the template

View file

@ -9,7 +9,7 @@ class CreateFarmRender(plugin.Creator):
name = "renderDefault"
label = "Render on Farm"
family = "renderFarm"
product_type = "renderFarm"
node_type = "WRITE"
def __init__(self, *args, **kwargs):

View file

@ -9,7 +9,7 @@ class CreateRender(plugin.Creator):
name = "renderDefault"
label = "Render"
family = "render"
product_type = "render"
node_type = "WRITE"
def __init__(self, *args, **kwargs):

View file

@ -6,7 +6,7 @@ class CreateTemplate(plugin.Creator):
name = "templateDefault"
label = "Template"
family = "harmony.template"
product_type = "harmony.template"
def __init__(self, *args, **kwargs):
super(CreateTemplate, self).__init__(*args, **kwargs)

View file

@ -45,12 +45,12 @@ class ImportAudioLoader(load.LoaderPlugin):
{"function": func, "args": [context["subset"]["name"], wav_file]}
)
subset_name = context["subset"]["name"]
product_name = context["subset"]["name"]
return harmony.containerise(
subset_name,
product_name,
namespace,
subset_name,
product_name,
context,
self.__class__.__name__
)

View file

@ -254,7 +254,7 @@ class BackgroundLoader(load.LoaderPlugin):
bg_folder = os.path.dirname(path)
subset_name = context["subset"]["name"]
product_name = context["subset"]["name"]
# read_node_name += "_{}".format(uuid.uuid4())
container_nodes = []
@ -272,9 +272,9 @@ class BackgroundLoader(load.LoaderPlugin):
container_nodes.append(read_node)
return harmony.containerise(
subset_name,
product_name,
namespace,
subset_name,
product_name,
context,
self.__class__.__name__,
nodes=container_nodes

View file

@ -47,7 +47,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
files.append(fname.parent.joinpath(remainder[0]).as_posix())
asset = context["asset"]["name"]
subset = context["subset"]["name"]
product_name = context["subset"]["name"]
group_id = str(uuid.uuid4())
read_node = harmony.send(
@ -56,7 +56,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
"args": [
files,
asset,
subset,
product_name,
1,
group_id
]
@ -64,7 +64,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
)["result"]
return harmony.containerise(
f"{asset}_{subset}",
f"{asset}_{product_name}",
namespace,
read_node,
context,

View file

@ -27,8 +27,8 @@ class ImportPaletteLoader(load.LoaderPlugin):
)
def load_palette(self, representation):
subset_name = representation["context"]["subset"]
name = subset_name.replace("palette", "")
product_name = representation["context"]["subset"]
name = product_name.replace("palette", "")
# Overwrite palette on disk.
scene_path = harmony.send(
@ -44,7 +44,7 @@ class ImportPaletteLoader(load.LoaderPlugin):
harmony.save_scene()
msg = "Updated {}.".format(subset_name)
msg = "Updated {}.".format(product_name)
msg += " You need to reload the scene to see the changes.\n"
msg += "Please save workfile when ready and use Workfiles "
msg += "to reopen it."

View file

@ -40,12 +40,12 @@ class ImportTemplateLoader(load.LoaderPlugin):
shutil.rmtree(temp_dir)
subset_name = context["subset"]["name"]
product_name = context["subset"]["name"]
return harmony.containerise(
subset_name,
product_name,
namespace,
subset_name,
product_name,
context,
self.__class__.__name__
)

View file

@ -80,7 +80,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
for frame in range(start, end + 1):
expected_files.append(
path / "{}-{}.{}".format(
render_instance.subset,
render_instance.productName,
str(frame).rjust(int(info[2]) + 1, "0"),
ext
)
@ -89,7 +89,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
return expected_files
def get_instances(self, context):
"""Get instances per Write node in `renderFarm` family."""
"""Get instances per Write node in `renderFarm` product type."""
version = None
if self.sync_workfile_version:
version = context.data["version"]
@ -111,7 +111,10 @@ class CollectFarmRender(publish.AbstractCollectRender):
if "container" in data["id"]:
continue
if data["family"] != "renderFarm":
product_type = data.get("productType")
if product_type is None:
product_type = data.get("family")
if product_type != "renderFarm":
continue
# 0 - filename / 1 - type / 2 - zeros / 3 - start / 4 - enabled
@ -124,15 +127,14 @@ class CollectFarmRender(publish.AbstractCollectRender):
# TODO: handle pixel aspect and frame step
# TODO: set Deadline stuff (pools, priority, etc. by presets)
# because of using 'renderFarm' as a family, replace 'Farm' with
# capitalized task name - issue of avalon-core Creator app
subset_name = node.split("/")[1]
task_name = context.data["anatomyData"]["task"][
"name"].capitalize()
# because of using 'renderFarm' as a product type, replace 'Farm'
# with capitalized task name - issue of Creator tool
product_name = node.split("/")[1]
task_name = context.data["task"].capitalize()
replace_str = ""
if task_name.lower() not in subset_name.lower():
if task_name.lower() not in product_name.lower():
replace_str = task_name
subset_name = subset_name.replace(
product_name = product_name.replace(
'Farm',
replace_str)
@ -141,7 +143,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
time=get_formatted_current_time(),
source=context.data["currentFile"],
label=node.split("/")[1],
subset=subset_name,
productName=product_name,
folderPath=folder_path,
task=task_name,
attachTo=False,
@ -151,6 +153,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
priority=50,
name=node.split("/")[1],
productType="render.farm",
family="render.farm",
families=["render.farm"],
farm=True,

View file

@ -19,7 +19,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
label = "Instances"
order = pyblish.api.CollectorOrder
hosts = ["harmony"]
families_mapping = {
product_type_mapping = {
"render": ["review", "ftrack"],
"harmony.template": [],
"palette": ["palette", "ftrack"]
@ -49,8 +49,14 @@ class CollectInstances(pyblish.api.ContextPlugin):
if "container" in data["id"]:
continue
# skip render farm family as it is collected separately
if data["family"] == "renderFarm":
product_type = data.get("productType")
if product_type is None:
product_type = data["family"]
data["productType"] = product_type
data["family"] = product_type
# skip render farm product type as it is collected separately
if product_type == "renderFarm":
continue
instance = context.create_instance(node.split("/")[-1])
@ -59,11 +65,14 @@ class CollectInstances(pyblish.api.ContextPlugin):
instance.data["publish"] = harmony.send(
{"function": "node.getEnable", "args": [node]}
)["result"]
instance.data["families"] = self.families_mapping[data["family"]]
families = [product_type]
families.extend(self.product_type_mapping[product_type])
instance.data["families"] = families
# If set in plugin, pair the scene Version in ftrack with
# thumbnails and review media.
if (self.pair_media and instance.data["family"] == "scene"):
if (self.pair_media and product_type == "scene"):
context.data["scene_instance"] = instance
# Produce diagnostic message for any graphical

View file

@ -33,14 +33,16 @@ class CollectPalettes(pyblish.api.ContextPlugin):
return
folder_path = context.data["folderPath"]
product_type = "harmony.palette"
for name, id in palettes.items():
instance = context.create_instance(name)
instance.data.update({
"id": id,
"family": "harmony.palette",
'families': [],
"productType": product_type,
"family": product_type,
"families": [product_type],
"folderPath": folder_path,
"subset": "{}{}".format("palette", name)
"productName": "{}{}".format("palette", name)
})
self.log.info(
"Created instance:\n" + json.dumps(

View file

@ -3,7 +3,7 @@
import os
import pyblish.api
from ayon_core.pipeline.create import get_subset_name
from ayon_core.pipeline.create import get_product_name
class CollectWorkfile(pyblish.api.ContextPlugin):
@ -15,26 +15,27 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
def process(self, context):
"""Plugin entry point."""
family = "workfile"
product_type = "workfile"
basename = os.path.basename(context.data["currentFile"])
subset = get_subset_name(
family,
"",
context.data["anatomyData"]["task"]["name"],
product_name = get_product_name(
context.data["projectName"],
context.data["assetEntity"],
context.data["anatomyData"]["project"]["name"],
host_name=context.data["hostName"],
context.data["task"],
context.data["hostName"],
product_type,
"",
project_settings=context.data["project_settings"]
)
# Create instance
instance = context.create_instance(subset)
instance = context.create_instance(product_name)
instance.data.update({
"subset": subset,
"productName": product_name,
"label": basename,
"name": basename,
"family": family,
"families": [family],
"productType": product_type,
"family": product_type,
"families": [product_type],
"representations": [],
"folderPath": context.data["folderPath"]
})

View file

@ -75,7 +75,9 @@ class ExtractTemplate(publish.Extractor):
instance.data["representations"] = [representation]
instance.data["version_name"] = "{}_{}".format(
instance.data["subset"], instance.context.data["task"])
instance.data["productName"],
instance.context.data["task"]
)
def get_backdrops(self, node: str) -> list:
"""Get backdrops for the node.

View file

@ -3,9 +3,9 @@
<error id="main">
<title>Subset context</title>
<description>
## Invalid subset context
## Invalid product context
Asset name found '{found}' in subsets, expected '{expected}'.
Asset name found '{found}' in products, expected '{expected}'.
### How to repair?
@ -19,7 +19,7 @@ If this is unwanted, close workfile and open again, that way different asset val
### __Detailed Info__ (optional)
This might happen if you are reuse old workfile and open it in different context.
(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.)
(Eg. you created product "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" asset stayed in the workfile.)
</detail>
</error>
</root>

View file

@ -589,8 +589,8 @@ def imprint(track_item, data=None):
Examples:
data = {
'asset': 'sq020sh0280',
'family': 'render',
'subset': 'subsetMain'
'productType': 'render',
'productName': 'productMain'
}
"""
data = data or {}

View file

@ -439,7 +439,7 @@ class ClipLoader:
""" Gets context and convert it to self.data
data structure:
{
"name": "assetName_subsetName_representationName"
"name": "assetName_productName_representationName"
"path": "path/to/file/created/by/get_repr..",
"binPath": "projectBinPath",
}
@ -448,10 +448,10 @@ class ClipLoader:
repr = self.context["representation"]
repr_cntx = repr["context"]
asset = str(repr_cntx["asset"])
subset = str(repr_cntx["subset"])
product_name = str(repr_cntx["subset"])
representation = str(repr_cntx["representation"])
self.data["clip_name"] = self.clip_name_template.format(**repr_cntx)
self.data["track_name"] = "_".join([subset, representation])
self.data["track_name"] = "_".join([product_name, representation])
self.data["versionData"] = self.context["version"]["data"]
# gets file path
file = get_representation_path_from_context(self.context)
@ -659,9 +659,9 @@ class PublishClip:
rename_default = False
hierarchy_default = "{_folder_}/{_sequence_}/{_track_}"
clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}"
subset_name_default = "<track_name>"
base_product_name_default = "<track_name>"
review_track_default = "< none >"
subset_family_default = "plate"
product_type_default = "plate"
count_from_default = 10
count_steps_default = 10
vertical_sync_default = False
@ -785,10 +785,10 @@ class PublishClip:
"countFrom", {}).get("value") or self.count_from_default
self.count_steps = self.ui_inputs.get(
"countSteps", {}).get("value") or self.count_steps_default
self.subset_name = self.ui_inputs.get(
"subsetName", {}).get("value") or self.subset_name_default
self.subset_family = self.ui_inputs.get(
"subsetFamily", {}).get("value") or self.subset_family_default
self.base_product_name = self.ui_inputs.get(
"productName", {}).get("value") or self.base_product_name_default
self.product_type = self.ui_inputs.get(
"productType", {}).get("value") or self.product_type_default
self.vertical_sync = self.ui_inputs.get(
"vSyncOn", {}).get("value") or self.vertical_sync_default
self.driving_layer = self.ui_inputs.get(
@ -798,12 +798,14 @@ class PublishClip:
self.audio = self.ui_inputs.get(
"audio", {}).get("value") or False
# build subset name from layer name
if self.subset_name == "<track_name>":
self.subset_name = self.track_name
# build product name from layer name
if self.base_product_name == "<track_name>":
self.base_product_name = self.track_name
# create subset for publishing
self.subset = self.subset_family + self.subset_name.capitalize()
# create product for publishing
self.product_name = (
self.product_type + self.base_product_name.capitalize()
)
def _replace_hash_to_expression(self, name, text):
""" Replace hash with number in correct padding. """
@ -885,14 +887,14 @@ class PublishClip:
for (_in, _out), hero_data in self.vertical_clip_match.items():
hero_data.update({"heroTrack": False})
if _in == self.clip_in and _out == self.clip_out:
data_subset = hero_data["subset"]
data_product_name = hero_data["productName"]
# add track index in case duplicity of names in hero data
if self.subset in data_subset:
hero_data["subset"] = self.subset + str(
if self.product_name in data_product_name:
hero_data["productName"] = self.product_name + str(
self.track_index)
# in case track name and subset name is the same then add
if self.subset_name == self.track_name:
hero_data["subset"] = self.subset
# in case track name and product name is the same then add
if self.base_product_name == self.track_name:
hero_data["productName"] = self.product_name
# assign data to return hierarchy data to tag
tag_hierarchy_data = hero_data
@ -913,9 +915,9 @@ class PublishClip:
"hierarchy": hierarchy_filled,
"parents": self.parents,
"hierarchyData": hierarchy_formatting_data,
"subset": self.subset,
"family": self.subset_family,
"families": [self.data["family"]]
"productName": self.product_name,
"productType": self.product_type,
"families": [self.product_type, self.data["family"]]
}
def _convert_to_entity(self, type, template):

View file

@ -28,8 +28,8 @@ def tag_data():
# "note": "Collecting track items to Nuke scripts.",
# "icon": "icons:TagNuke.png",
# "metadata": {
# "family": "nukescript",
# "subset": "main"
# "productType": "nukescript",
# "productName": "main"
# }
# },
"Comment": {
@ -37,17 +37,17 @@ def tag_data():
"note": "Comment on a shot.",
"icon": "icons:TagComment.png",
"metadata": {
"family": "comment",
"subset": "main"
"productType": "comment",
"productName": "main"
}
},
"FrameMain": {
"editable": "1",
"note": "Publishing a frame subset.",
"note": "Publishing a frame product.",
"icon": "z_layer_main.png",
"metadata": {
"family": "frame",
"subset": "main",
"productType": "frame",
"productName": "main",
"format": "png"
}
}
@ -153,7 +153,7 @@ def add_tags_to_workfile():
"note": task_type,
"icon": "icons:TagGood.png",
"metadata": {
"family": "task",
"productType": "task",
"type": task_type
}
}
@ -173,7 +173,7 @@ def add_tags_to_workfile():
"path": "icons:TagActor.png"
},
"metadata": {
"family": "assetbuild"
"productType": "assetbuild"
}
}

View file

@ -10,7 +10,7 @@ class CreateShotClip(phiero.Creator):
"""Publishable clip"""
label = "Create Publishable Clip"
family = "clip"
product_type = "clip"
icon = "film"
defaults = ["Main"]
@ -133,19 +133,19 @@ class CreateShotClip(phiero.Creator):
"target": "ui",
"order": 3,
"value": {
"subsetName": {
"productName": {
"value": ["<track_name>", "main", "bg", "fg", "bg",
"animatic"],
"type": "QComboBox",
"label": "Subset Name",
"label": "pRODUCT Name",
"target": "ui",
"toolTip": "chose subset name pattern, if <track_name> is selected, name of track layer will be used", # noqa
"toolTip": "chose product name pattern, if <track_name> is selected, name of track layer will be used", # noqa
"order": 0},
"subsetFamily": {
"productType": {
"value": ["plate", "take"],
"type": "QComboBox",
"label": "Subset Family",
"target": "ui", "toolTip": "What use of this subset is for", # noqa
"label": "Product Type",
"target": "ui", "toolTip": "What use of this product is for", # noqa
"order": 1},
"reviewTrack": {
"value": ["< none >"] + gui_tracks,
@ -159,7 +159,7 @@ class CreateShotClip(phiero.Creator):
"type": "QCheckBox",
"label": "Include audio",
"target": "tag",
"toolTip": "Process subsets with corresponding audio", # noqa
"toolTip": "Process productS with corresponding audio", # noqa
"order": 3},
"sourceResolution": {
"value": False,

View file

@ -14,7 +14,7 @@ import ayon_core.hosts.hiero.api as phiero
class LoadClip(phiero.SequenceLoader):
"""Load a subset to timeline as clip
"""Load a product to timeline as clip
Place clip to timeline on its asset origin timings collected
during conforming to project

View file

@ -1,3 +1,4 @@
from itertools import product
import re
import pyblish.api
@ -12,13 +13,13 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
effect_categories = []
def process(self, instance):
family = "effect"
product_type = "effect"
effects = {}
review = instance.data.get("review")
review_track_index = instance.context.data.get("reviewTrackIndex")
item = instance.data["item"]
if "audio" in instance.data["family"]:
if "audio" in instance.data["productType"]:
return
# frame range
@ -61,16 +62,16 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
if not effects:
return
subset = instance.data.get("subset")
effects.update({"assignTo": subset})
product_name = instance.data.get("productName")
effects.update({"assignTo": product_name})
subset_split = re.findall(r'[A-Z][^A-Z]*', subset)
product_name_split = re.findall(r'[A-Z][^A-Z]*', product_name)
if len(subset_split) > 0:
root_name = subset.replace(subset_split[0], "")
subset_split.insert(0, root_name.capitalize())
if len(product_name_split) > 0:
root_name = product_name.replace(product_name_split[0], "")
product_name_split.insert(0, root_name.capitalize())
subset_split.insert(0, "effect")
product_name_split.insert(0, "effect")
effect_categories = {
x["name"]: x["effect_classes"] for x in self.effect_categories
@ -104,8 +105,8 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
effects_categorized[category]["assignTo"] = effects["assignTo"]
for category, effects in effects_categorized.items():
name = "".join(subset_split)
name += category.capitalize()
product_name = "".join(product_name_split)
product_name += category.capitalize()
# create new instance and inherit data
data = {}
@ -114,15 +115,17 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
continue
data[key] = value
# change names
data["subset"] = name
data["family"] = family
data["families"] = [family]
data["name"] = data["subset"] + "_" + data["folderPath"]
data["label"] = "{} - {}".format(
data["folderPath"], data["subset"]
)
data["effects"] = effects
data.update({
"productName": product_name,
"productType": product_type,
"family": product_type,
"families": [product_type],
"name": product_name + "_" + data["folderPath"],
"label": "{} - {}".format(
data["folderPath"], product_name
),
"effects": effects,
})
# create new instance
_instance = instance.context.create_instance(**data)

View file

@ -13,8 +13,8 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
Tag is expected to have metadata:
{
"family": "frame"
"subset": "main"
"productType": "frame"
"productName": "main"
}
"""
@ -26,14 +26,14 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
self._context = context
# collect all sequence tags
subset_data = self._create_frame_subset_data_sequence(context)
product_data = self._create_frame_product_data_sequence(context)
self.log.debug("__ subset_data: {}".format(
pformat(subset_data)
self.log.debug("__ product_data: {}".format(
pformat(product_data)
))
# create instances
self._create_instances(subset_data)
self._create_instances(product_data)
def _get_tag_data(self, tag):
data = {}
@ -66,7 +66,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
return data
def _create_frame_subset_data_sequence(self, context):
def _create_frame_product_data_sequence(self, context):
sequence_tags = []
sequence = context.data["activeTimeline"]
@ -87,10 +87,13 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
if not tag_data:
continue
if "family" not in tag_data:
product_type = tag_data.get("productType")
if product_type is None:
product_type = tag_data.get("family")
if not product_type:
continue
if tag_data["family"] != "frame":
if product_type != "frame":
continue
sequence_tags.append(tag_data)
@ -99,8 +102,8 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
pformat(sequence_tags)
))
# first collect all available subset tag frames
subset_data = {}
# first collect all available product tag frames
product_data = {}
context_asset_doc = context.data["assetEntity"]
context_folder_path = get_asset_name_identifier(context_asset_doc)
@ -110,33 +113,37 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
if frame not in publish_frames:
continue
subset = tag_data["subset"]
product_name = tag_data.get("productName")
if product_name is None:
product_name = tag_data["subset"]
if subset in subset_data:
# update existing subset key
subset_data[subset]["frames"].append(frame)
if product_name in product_data:
# update existing product key
product_data[product_name]["frames"].append(frame)
else:
# create new subset key
subset_data[subset] = {
# create new product key
product_data[product_name] = {
"frames": [frame],
"format": tag_data["format"],
"folderPath": context_folder_path
}
return subset_data
return product_data
def _create_instances(self, subset_data):
# create instance per subset
for subset_name, subset_data in subset_data.items():
name = "frame" + subset_name.title()
def _create_instances(self, product_data):
# create instance per product
product_type = "image"
for product_name, product_data in product_data.items():
name = "frame" + product_name.title()
data = {
"name": name,
"label": "{} {}".format(name, subset_data["frames"]),
"family": "image",
"families": ["frame"],
"folderPath": subset_data["folderPath"],
"subset": name,
"format": subset_data["format"],
"frames": subset_data["frames"]
"label": "{} {}".format(name, product_data["frames"]),
"productType": product_type,
"family": product_type,
"families": [product_type, "frame"],
"folderPath": product_data["folderPath"],
"productName": name,
"format": product_data["format"],
"frames": product_data["frames"]
}
self._context.create_instance(**data)

View file

@ -16,10 +16,12 @@ class CollectClipTagTasks(api.InstancePlugin):
tasks = {}
for tag in tags:
t_metadata = dict(tag.metadata())
t_family = t_metadata.get("tag.family", "")
t_product_type = t_metadata.get("tag.productType")
if t_product_type is None:
t_product_type = t_metadata.get("tag.family", "")
# gets only task family tags and collect labels
if "task" in t_family:
# gets only task product type tags and collect labels
if "task" in t_product_type:
t_task_name = t_metadata.get("tag.label", "")
t_task_type = t_metadata.get("tag.type", "")
tasks[t_task_name] = {"type": t_task_type}

View file

@ -21,8 +21,8 @@ class ExtractClipEffects(publish.Extractor):
if not effects:
return
subset = instance.data.get("subset")
family = instance.data["family"]
product_name = instance.data.get("productName")
product_type = instance.data["productType"]
self.log.debug("creating staging dir")
staging_dir = self.staging_dir(instance)
@ -32,7 +32,7 @@ class ExtractClipEffects(publish.Extractor):
instance.data["transfers"] = list()
ext = "json"
file = subset + "." + ext
file = product_name + "." + ext
# when instance is created during collection part
resources_dir = instance.data["resourcesDir"]
@ -68,8 +68,10 @@ class ExtractClipEffects(publish.Extractor):
version_data.update({
"colorspace": item.sourceMediaColourTransform(),
"colorspaceScript": instance.context.data["colorspace"],
"families": [family, "plate"],
"subset": subset,
"families": [product_type, "plate"],
# TODO find out if 'subset' is needed (and 'productName')
"subset": product_name,
"productName": product_name,
"fps": instance.context.data["fps"]
})
instance.data["versionData"] = version_data
@ -77,7 +79,7 @@ class ExtractClipEffects(publish.Extractor):
representation = {
'files': file,
'stagingDir': staging_dir,
'name': family + ext.title(),
'name': product_type + ext.title(),
'ext': ext
}
instance.data["representations"].append(representation)

View file

@ -87,22 +87,47 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
asset, asset_name = self._get_asset_data(tag_data)
subset = tag_data["subset"]
product_name = tag_data.get("productName")
if product_name is None:
product_name = tag_data["subset"]
# insert family into families
families = [str(f) for f in tag_data["families"]]
# form label
label = "{} -".format(asset)
if asset_name != clip_name:
label += " ({})".format(clip_name)
label += " {}".format(subset)
label += " {}".format(product_name)
# TODO: remove backward compatibility
product_name = tag_data.get("productName")
if product_name is None:
# backward compatibility: subset -> productName
product_name = tag_data.get("subset")
# backward compatibility: product_name should not be missing
if not product_name:
self.log.error(
"Product name is not defined for: {}".format(asset))
# TODO: remove backward compatibility
product_type = tag_data.get("productType")
if product_type is None:
# backward compatibility: family -> productType
product_type = tag_data.get("family")
# backward compatibility: product_type should not be missing
if not product_type:
self.log.error(
"Product type is not defined for: {}".format(asset))
data.update({
"name": "{}_{}".format(asset, subset),
"name": "{}_{}".format(asset, product_name),
"label": label,
"folderPath": asset,
"asset_name": asset_name,
"productName": product_name,
"productType": product_type,
"item": track_item,
"families": families,
"publish": tag_data["publish"],
@ -146,7 +171,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
if not with_audio:
continue
# create audio subset instance
# create audio product instance
self.create_audio_instance(context, **data)
# add audioReview attribute to plate instance data
@ -180,7 +205,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
})
def create_shot_instance(self, context, **data):
subset = "shotMain"
product_name = "shotMain"
master_layer = data.get("heroTrack")
hierarchy_data = data.get("hierarchyData")
item = data.get("item")
@ -195,21 +220,21 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
asset = data["folderPath"]
asset_name = data["asset_name"]
# insert family into families
family = "shot"
product_type = "shot"
# form label
label = "{} -".format(asset)
if asset_name != clip_name:
label += " ({}) ".format(clip_name)
label += " {}".format(subset)
label += " {}".format(product_name)
data.update({
"name": "{}_{}".format(asset, subset),
"name": "{}_{}".format(asset, product_name),
"label": label,
"subset": subset,
"family": family,
"families": []
"productName": product_name,
"productType": product_type,
"family": product_type,
"families": [product_type]
})
instance = context.create_instance(**data)
@ -238,7 +263,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
return folder_path, asset_name
def create_audio_instance(self, context, **data):
subset = "audioMain"
product_name = "audioMain"
master_layer = data.get("heroTrack")
if not master_layer:
@ -254,21 +279,21 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
asset = data["folderPath"]
asset_name = data["asset_name"]
# insert family into families
family = "audio"
product_type = "audio"
# form label
label = "{} -".format(asset)
if asset_name != clip_name:
label += " ({}) ".format(clip_name)
label += " {}".format(subset)
label += " {}".format(product_name)
data.update({
"name": "{}_{}".format(asset, subset),
"name": "{}_{}".format(asset, product_name),
"label": label,
"subset": subset,
"family": family,
"families": ["clip"]
"productName": product_name,
"productType": product_type,
"family": product_type,
"families": [product_type, "clip"]
})
# remove review track attr if any
data.pop("reviewTrack")

View file

@ -59,17 +59,20 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin):
'files': base_name,
"stagingDir": staging_dir,
}
family = "workfile"
product_type = "workfile"
instance_data = {
"label": "{} - {}Main".format(
asset, family),
"name": "{}_{}".format(asset_name, family),
asset, product_type),
"name": "{}_{}".format(asset_name, product_type),
"folderPath": context.data["folderPath"],
# TODO use 'get_subset_name'
"subset": "{}{}Main".format(asset_name, family.capitalize()),
# TODO use 'get_product_name'
"productName": "{}{}Main".format(
asset_name, product_type.capitalize()
),
"item": project,
"family": family,
"families": [],
"productType": product_type,
"family": product_type,
"families": [product_type],
"representations": [workfile_representation, thumb_representation]
}

View file

@ -8,7 +8,7 @@ class CollectAssetBuilds(api.ContextPlugin):
Tag is expected to have name of the asset and metadata:
{
"family": "assetbuild"
"productType": "assetbuild"
}
"""
@ -29,7 +29,7 @@ class CollectAssetBuilds(api.ContextPlugin):
asset_builds[asset_name] = asset_doc
for instance in context:
if instance.data["family"] != "clip":
if instance.data["productType"] != "clip":
continue
# Exclude non-tagged instances.
@ -38,9 +38,11 @@ class CollectAssetBuilds(api.ContextPlugin):
for tag in instance.data["tags"]:
t_metadata = dict(tag.metadata())
t_family = t_metadata.get("tag.family", "")
t_product_type = t_metadata.get("tag.productType")
if t_product_type is None:
t_product_type = t_metadata.get("tag.family", "")
if t_family.lower() == "assetbuild":
if t_product_type.lower() == "assetbuild":
asset_names.append(tag["name"])
tagged = True

View file

@ -990,7 +990,7 @@ class FileDescriptor(DescriptorBase):
:class:`descriptor_pb2.FileDescriptorProto`.
dependencies (list[FileDescriptor]): List of other :class:`FileDescriptor`
objects this :class:`FileDescriptor` depends on.
public_dependencies (list[FileDescriptor]): A subset of
public_dependencies (list[FileDescriptor]): A product of
:attr:`dependencies`, which were declared as "public".
message_types_by_name (dict(str, Descriptor)): Mapping from message names
to their :class:`Descriptor`.

View file

@ -90,21 +90,21 @@ def create_interactive(creator_identifier, **kwargs):
pane = stateutils.activePane(kwargs)
if isinstance(pane, hou.NetworkEditor):
pwd = pane.pwd()
subset_name = creator.get_subset_name(
variant=variant,
task_name=context.get_current_task_name(),
product_name = creator.get_product_name(
project_name=context.get_current_project_name(),
asset_doc=get_asset_by_name(
project_name=context.get_current_project_name(),
asset_name=context.get_current_asset_name()
),
project_name=context.get_current_project_name(),
host_name=context.host_name
task_name=context.get_current_task_name(),
variant=variant,
host_name=context.host_name,
)
tool_fn = CATEGORY_GENERIC_TOOL.get(pwd.childTypeCategory())
if tool_fn is not None:
out_null = tool_fn(kwargs, "null")
out_null.setName("OUT_{}".format(subset_name), unique_name=True)
out_null.setName("OUT_{}".format(product_name), unique_name=True)
before = context.instances_by_id.copy()

View file

@ -99,24 +99,21 @@ class Creator(LegacyCreator):
class HoudiniCreatorBase(object):
@staticmethod
def cache_subsets(shared_data):
def cache_instance_data(shared_data):
"""Cache instances for Creators to shared data.
Create `houdini_cached_subsets` key when needed in shared data and
Create `houdini_cached_instances` key when needed in shared data and
fill it with all collected instances from the scene under its
respective creator identifiers.
Create `houdini_cached_legacy_subsets` key for any legacy instances
Create `houdini_cached_legacy_instance` key for any legacy instances
detected in the scene as instances per family.
Args:
Dict[str, Any]: Shared data.
Return:
Dict[str, Any]: Shared data dictionary.
"""
if shared_data.get("houdini_cached_subsets") is None:
if shared_data.get("houdini_cached_instances") is None:
cache = dict()
cache_legacy = dict()
@ -141,8 +138,8 @@ class HoudiniCreatorBase(object):
family = family_parm.eval()
cache_legacy.setdefault(family, []).append(node)
shared_data["houdini_cached_subsets"] = cache
shared_data["houdini_cached_legacy_subsets"] = cache_legacy
shared_data["houdini_cached_instances"] = cache
shared_data["houdini_cached_legacy_instance"] = cache_legacy
return shared_data
@ -177,7 +174,7 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
settings_name = None
add_publish_button = False
def create(self, subset_name, instance_data, pre_create_data):
def create(self, product_name, instance_data, pre_create_data):
try:
self.selected_nodes = []
@ -192,15 +189,15 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
asset_name = instance_data["folderPath"]
instance_node = self.create_instance_node(
asset_name, subset_name, "/out", node_type)
asset_name, product_name, "/out", node_type)
self.customize_node_look(instance_node)
instance_data["instance_node"] = instance_node.path()
instance_data["instance_id"] = instance_node.path()
instance = CreatedInstance(
self.family,
subset_name,
self.product_type,
product_name,
instance_data,
self)
self._add_instance_to_context(instance)
@ -234,9 +231,9 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
def collect_instances(self):
# cache instances if missing
self.cache_subsets(self.collection_shared_data)
self.cache_instance_data(self.collection_shared_data)
for instance in self.collection_shared_data[
"houdini_cached_subsets"].get(self.identifier, []):
"houdini_cached_instances"].get(self.identifier, []):
node_data = read(instance)
@ -246,6 +243,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
node_path = instance.path()
node_data["instance_id"] = node_path
node_data["instance_node"] = node_path
if "AYON_productName" in node_data:
node_data["productName"] = node_data.pop("AYON_productName")
created_instance = CreatedInstance.from_existing(
node_data, self
@ -269,6 +268,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
def imprint(self, node, values, update=False):
# Never store instance node and instance id since that data comes
# from the node's path
if "productName" in values:
values["AYON_productName"] = values.pop("productName")
values.pop("instance_node", None)
values.pop("instance_id", None)
imprint(node, values, update=update)

View file

@ -1,24 +1,24 @@
# -*- coding: utf-8 -*-
"""Converter for legacy Houdini subsets."""
"""Converter for legacy Houdini products."""
from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin
from ayon_core.hosts.houdini.api.lib import imprint
class HoudiniLegacyConvertor(SubsetConvertorPlugin):
"""Find and convert any legacy subsets in the scene.
"""Find and convert any legacy products in the scene.
This Converter will find all legacy subsets in the scene and will
transform them to the current system. Since the old subsets doesn't
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.
Its limitation is that you can have multiple creators creating subset
of the same family and there is no way to handle it. This code should
nevertheless cover all creators that came with OpenPype.
Its limitation is that you can have multiple creators creating product
name of the same product type and there is no way to handle it. This code
should nevertheless cover all creators that came with AYON.
"""
identifier = "io.openpype.creators.houdini.legacy"
family_to_id = {
product_type_to_id = {
"camera": "io.openpype.creators.houdini.camera",
"ass": "io.openpype.creators.houdini.ass",
"imagesequence": "io.openpype.creators.houdini.imagesequence",
@ -33,44 +33,46 @@ class HoudiniLegacyConvertor(SubsetConvertorPlugin):
def __init__(self, *args, **kwargs):
super(HoudiniLegacyConvertor, self).__init__(*args, **kwargs)
self.legacy_subsets = {}
self.legacy_instances = {}
def find_instances(self):
"""Find legacy subsets in the scene.
"""Find legacy products in the scene.
Legacy subsets are the ones that doesn't have `creator_identifier`
Legacy products are the ones that doesn't have `creator_identifier`
parameter on them.
This is using cached entries done in
:py:meth:`~HoudiniCreatorBase.cache_subsets()`
:py:meth:`~HoudiniCreatorBase.cache_instance_data()`
"""
self.legacy_subsets = self.collection_shared_data.get(
"houdini_cached_legacy_subsets")
if not self.legacy_subsets:
self.legacy_instances = self.collection_shared_data.get(
"houdini_cached_legacy_instance")
if not self.legacy_instances:
return
self.add_convertor_item("Found {} incompatible subset{}.".format(
len(self.legacy_subsets), "s" if len(self.legacy_subsets) > 1 else "")
)
self.add_convertor_item("Found {} incompatible product{}.".format(
len(self.legacy_instances),
"s" if len(self.legacy_instances) > 1 else ""
))
def convert(self):
"""Convert all legacy subsets to current.
"""Convert all legacy products to current.
It is enough to add `creator_identifier` and `instance_node`.
"""
if not self.legacy_subsets:
if not self.legacy_instances:
return
for family, subsets in self.legacy_subsets.items():
if family in self.family_to_id:
for subset in subsets:
for product_type, legacy_instances in self.legacy_instances.items():
if product_type in self.product_type_to_id:
for instance in legacy_instances:
creator_id = self.product_type_to_id[product_type]
data = {
"creator_identifier": self.family_to_id[family],
"instance_node": subset.path()
"creator_identifier": creator_id,
"instance_node": instance.path()
}
if family == "pointcache":
if product_type == "pointcache":
data["families"] = ["abc"]
self.log.info("Converting {} to {}".format(
subset.path(), self.family_to_id[family]))
imprint(subset, data)
instance.path(), creator_id))
imprint(instance, data)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating alembic camera subsets."""
"""Creator plugin for creating alembic camera products."""
from ayon_core.hosts.houdini.api import plugin
from ayon_core.pipeline import CreatedInstance, CreatorError
@ -11,24 +11,24 @@ class CreateAlembicCamera(plugin.HoudiniCreator):
identifier = "io.openpype.creators.houdini.camera"
label = "Camera (Abc)"
family = "camera"
product_type = "camera"
icon = "camera"
def create(self, subset_name, instance_data, pre_create_data):
def create(self, product_name, instance_data, pre_create_data):
import hou
instance_data.pop("active", None)
instance_data.update({"node_type": "alembic"})
instance = super(CreateAlembicCamera, self).create(
subset_name,
product_name,
instance_data,
pre_create_data) # type: CreatedInstance
instance_node = hou.node(instance.get("instance_node"))
parms = {
"filename": hou.text.expandString(
"$HIP/pyblish/{}.abc".format(subset_name)),
"$HIP/pyblish/{}.abc".format(product_name)),
"use_sop_path": False,
}

View file

@ -9,7 +9,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
identifier = "io.openpype.creators.houdini.ass"
label = "Arnold ASS"
family = "ass"
product_type = "ass"
icon = "magic"
# Default extension: `.ass` or `.ass.gz`
@ -17,7 +17,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
# will override it by the value in the project settings
ext = ".ass"
def create(self, subset_name, instance_data, pre_create_data):
def create(self, product_name, instance_data, pre_create_data):
import hou
instance_data.pop("active", None)
@ -27,7 +27,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
creator_attributes["farm"] = pre_create_data["farm"]
instance = super(CreateArnoldAss, self).create(
subset_name,
product_name,
instance_data,
pre_create_data) # type: plugin.CreatedInstance
@ -41,7 +41,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
filepath = "{}{}".format(
hou.text.expandString("$HIP/pyblish/"),
"{}.$F4{}".format(subset_name, self.ext)
"{}.$F4{}".format(product_name, self.ext)
)
parms = {
# Render frame range
@ -54,7 +54,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
instance_node.setParms(parms)
# Lock any parameters in this list
to_lock = ["ar_ass_export_enable", "family", "id"]
to_lock = ["ar_ass_export_enable", "productType", "id"]
self.lock_parameters(instance_node, to_lock)
def get_instance_attr_defs(self):

View file

@ -7,7 +7,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
identifier = "io.openpype.creators.houdini.arnold_rop"
label = "Arnold ROP"
family = "arnold_rop"
product_type = "arnold_rop"
icon = "magic"
# Default extension
@ -16,7 +16,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
# Default to split export and render jobs
export_job = True
def create(self, subset_name, instance_data, pre_create_data):
def create(self, product_name, instance_data, pre_create_data):
import hou
# Remove the active, we are checking the bypass flag of the nodes
@ -29,7 +29,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
instance_data["farm"] = pre_create_data.get("farm")
instance = super(CreateArnoldRop, self).create(
subset_name,
product_name,
instance_data,
pre_create_data) # type: plugin.CreatedInstance
@ -37,9 +37,9 @@ class CreateArnoldRop(plugin.HoudiniCreator):
ext = pre_create_data.get("image_format")
filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format(
filepath = "{renders_dir}{product_name}/{product_name}.$F4.{ext}".format(
renders_dir=hou.text.expandString("$HIP/pyblish/renders/"),
subset_name=subset_name,
product_name=product_name,
ext=ext,
)
parms = {
@ -53,9 +53,9 @@ class CreateArnoldRop(plugin.HoudiniCreator):
if pre_create_data.get("export_job"):
ass_filepath = \
"{export_dir}{subset_name}/{subset_name}.$F4.ass".format(
"{export_dir}{product_name}/{product_name}.$F4.ass".format(
export_dir=hou.text.expandString("$HIP/pyblish/ass/"),
subset_name=subset_name,
product_name=product_name,
)
parms["ar_ass_export_enable"] = 1
parms["ar_ass_file"] = ass_filepath
@ -63,7 +63,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
instance_node.setParms(parms)
# Lock any parameters in this list
to_lock = ["family", "id"]
to_lock = ["productType", "id"]
self.lock_parameters(instance_node, to_lock)
def get_pre_create_attr_defs(self):

Some files were not shown because too many files have changed in this diff Show more