diff --git a/client/ayon_core/hosts/fusion/api/plugin.py b/client/ayon_core/hosts/fusion/api/plugin.py index 3bf810ca23..95db8126e7 100644 --- a/client/ayon_core/hosts/fusion/api/plugin.py +++ b/client/ayon_core/hosts/fusion/api/plugin.py @@ -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""" diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py b/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py index 856d86cff6..8110898ae9 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_image_saver.py @@ -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 diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py index 1a0dad7060..b5abb2d949 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_saver.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_saver.py @@ -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" diff --git a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py index 08d39b0145..1f49d4bbc5 100644 --- a/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py +++ b/client/ayon_core/hosts/fusion/plugins/create/create_workfile.py @@ -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,7 +78,7 @@ class FusionWorkfileCreator(AutoCreator): if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) @@ -90,7 +93,7 @@ class FusionWorkfileCreator(AutoCreator): )) 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 +103,10 @@ 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( + product_name = self.get_product_name( self.default_variant, task_name, asset_doc, project_name, host_name ) existing_instance["folderPath"] = asset_name existing_instance["task"] = task_name - existing_instance["subset"] = subset_name + existing_instance["productName"] = product_name diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py index a0131248e8..2cbd4d82f4 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_instances.py @@ -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), diff --git a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py index 0a0e4b38af..0eea061827 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/collect_render.py @@ -49,17 +49,17 @@ 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, + productType=product_type, tool=tool, workfileComp=comp, families=instance_families, @@ -67,13 +67,13 @@ class CollectFusionRender( 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, diff --git a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py index 3131400de9..939ddbd117 100644 --- a/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -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 + )