diff --git a/client/ayon_hiero/api/plugin.py b/client/ayon_hiero/api/plugin.py index 6ecef54..bc98d2f 100644 --- a/client/ayon_hiero/api/plugin.py +++ b/client/ayon_hiero/api/plugin.py @@ -673,7 +673,7 @@ class PublishClip: hierarchy_default = "{_folder_}/{_sequence_}/{_track_}" clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}" product_name_default = "" - review_track_default = "< none >" + review_source_default = None product_type_default = "plate" count_from_default = 10 count_steps_default = 10 @@ -689,7 +689,9 @@ class PublishClip: # publish settings "audio", "sourceResolution", # shot attributes - "workfileFrameStart", "handleStart", "handleEnd" + "workfileFrameStart", "handleStart", "handleEnd", + # instance attributes data + "reviewableSource", } def __init__( @@ -743,8 +745,9 @@ def convert(self): # if track name is in review track name and also if driving track name # is not in review track name: skip tag creation - if (self.track_name in self.review_layer) and ( - self.driving_layer not in self.review_layer): + if (self.track_name in self.reviewable_source) and ( + self.driving_layer not in self.reviewable_source + ): return # deal with clip name @@ -765,11 +768,6 @@ def convert(self): ) self.tag_data["folderPath"] = folder_path - if self.tag_data["heroTrack"] and self.review_layer: - self.tag_data.update({"reviewTrack": self.review_layer}) - else: - self.tag_data.update({"reviewTrack": None}) - return self.track_item def _populate_track_item_default_data(self): @@ -795,7 +793,7 @@ def _populate_attributes(self): log.debug( "____ self.shot_num: {}".format(self.shot_num)) - # publisher ui attribute inputs or default values if gui was not used + # publisher ui attribute inputs or default values if gui was not used def get(key): """Shorthand access for code readability""" return self.pre_create_data.get(key) @@ -810,7 +808,8 @@ def get(key): self.product_type = get("productType") or self.product_type_default self.vertical_sync = get("vSyncOn") or self.vertical_sync_default self.driving_layer = get("vSyncTrack") or self.driving_layer_default - self.review_track = get("reviewTrack") or self.review_track_default + self.review_source = ( + get("reviewableSource") or self.review_source_default) self.audio = get("audio") or False self.hierarchy_data = { @@ -841,7 +840,7 @@ def _convert_to_tag_data(self): """ # define vertical sync attributes hero_track = True - self.review_layer = "" + self.reviewable_source = "" if self.vertical_sync: # check if track name is not in driving layer if self.track_name not in self.driving_layer: @@ -857,16 +856,23 @@ def _convert_to_tag_data(self): # adding tag metadata from ui for _key, _value in self.pre_create_data.items(): + # backward compatibility for reviewableSource (2024.11.08) + if ( + _key == "reviewableSource" + and "reviewTrack" in self.tag_keys + ): + self.tag_data.pop("reviewTrack") + self.tag_data["reviewableSource"] = _value if _key in self.tag_keys: self.tag_data[_key] = _value # driving layer is set as positive match if hero_track or self.vertical_sync: # mark review layer - if self.review_track and ( - self.review_track not in self.review_track_default): + if self.review_source and ( + self.review_source != self.review_source_default): # if review layer is defined and not the same as default - self.review_layer = self.review_track + self.reviewable_source = self.review_source # shot num calculate if self.rename_index == 0: self.shot_num = self.count_from @@ -924,11 +930,30 @@ def _convert_to_tag_data(self): self.tag_data["uuid"] = str(uuid.uuid4()) # add review track only to hero track - if hero_track and self.review_layer: - self.tag_data["reviewTrack"] = self.review_layer + if hero_track and self.reviewable_source: + self.tag_data["reviewTrack"] = self.reviewable_source else: self.tag_data.update({"reviewTrack": None}) + # add only review related data if reviewable source is set + if self.reviewable_source: + review_switch = True + reviewable_source = self.reviewable_source + # + if self.vertical_sync and not hero_track: + review_switch = False + reviewable_source = False + + if review_switch: + self.tag_data["review"] = True + else: + self.tag_data.pop("review", None) + + if reviewable_source: + self.tag_data["reviewableSource"] = reviewable_source + else: + self.tag_data.pop("reviewableSource", None) + def _solve_tag_hierarchy_data(self, hierarchy_formatting_data): """ Solve tag data from hierarchy data and templates. """ # fill up clip name and hierarchy keys diff --git a/client/ayon_hiero/plugins/create/create_shot_clip.py b/client/ayon_hiero/plugins/create/create_shot_clip.py index 38d772e..05f6383 100644 --- a/client/ayon_hiero/plugins/create/create_shot_clip.py +++ b/client/ayon_hiero/plugins/create/create_shot_clip.py @@ -176,11 +176,43 @@ class _HieroInstanceClipCreatorBase(_HieroInstanceCreator): """ Base clip product creator. """ - def get_instance_attr_defs(self): + def register_callbacks(self): + self.create_context.add_value_changed_callback(self._on_value_change) + + def _on_value_change(self, event): + for item in event["changes"]: + instance = item["instance"] + if ( + instance is None + or instance.creator_identifier != self.identifier + ): + continue + + changes = item["changes"].get("creator_attributes", {}) + + attr_defs = instance.creator_attributes.attr_defs + + if "review" not in changes: + continue + + review_value = changes["review"] + reviewable_source = next( + attr_def + for attr_def in attr_defs + if attr_def.key == "reviewableSource" + ) + reviewable_source.disabled = not review_value + + instance.set_create_attr_defs(attr_defs) + + def get_attr_defs_for_instance(self, instance): current_sequence = lib.get_current_sequence() if current_sequence is not None: - gui_tracks = [tr.name() for tr in current_sequence.videoTracks()] + gui_tracks = [ + {"value": tr.name(), "label": f"Track: {tr.name()}"} + for tr in current_sequence.videoTracks() + ] else: gui_tracks = [] @@ -192,23 +224,34 @@ def get_instance_attr_defs(self): ) ] if self.product_type == "plate": - instance_attributes.extend([ - BoolDef( - "vSyncOn", - label="Enable Vertical Sync", - tooltip="Switch on if you want clips above " - "each other to share its attributes", - default=True, - ), - EnumDef( - "vSyncTrack", - label="Hero Track", - tooltip="Select driving track name which should " - "be mastering all others", - items=gui_tracks or [""], - ), - ]) - + # Review track visibility + current_review = instance.creator_attributes.get("review", False) + + instance_attributes.extend( + [ + BoolDef( + "review", + label="Review", + tooltip="Switch to reviewable instance", + default=False, + ), + EnumDef( + "reviewableSource", + label="Reviewable Source", + tooltip=("Selecting source for reviewable files."), + items=( + [ + { + "value": "clip_media", + "label": "[ Clip's media ]", + }, + ] + + gui_tracks + ), + disabled=not current_review, + ), + ] + ) return instance_attributes @@ -271,7 +314,10 @@ def header_label(text): current_sequence = lib.get_current_sequence() if current_sequence is not None: - gui_tracks = [tr.name() for tr in current_sequence.videoTracks()] + gui_tracks = [ + {"value": tr.name(), "label": f"Track: {tr.name()}"} + for tr in current_sequence.videoTracks() + ] else: gui_tracks = [] @@ -379,9 +425,12 @@ def header_label(text): label="Hero track", tooltip="Select driving track name which should " "be mastering all others", - items=gui_tracks or [""], + items=( + gui_tracks or [ + {"value": None, "label": "< nothing to select> "} + ] + ), ), - # publishSettings UILabelDef( label=header_label("Publish Settings") @@ -401,11 +450,15 @@ def header_label(text): items=['plate', 'take'], ), EnumDef( - "reviewTrack", - label="Use Review Track", + "reviewableSource", + label="Reviewable Source", tooltip="Generate preview videos on fly, if " - "'< none >' is defined nothing will be generated.", - items=['< none >'] + gui_tracks, + "'< none >' is defined nothing will be generated.", + items=[ + {"value": None, "label": "< none >"}, + {"value": "clip_media", "label": "[ Clip's media ]"}, + ] + + gui_tracks, ), BoolDef( "export_audio", @@ -416,10 +469,9 @@ def header_label(text): BoolDef( "sourceResolution", label="Source resolution", - tooltip="Is resloution taken from timeline or source?", + tooltip="Is resolution taken from timeline or source?", default=False, ), - # shotAttr UILabelDef( label=header_label("Shot Attributes"), @@ -564,16 +616,23 @@ def create(self, subset_name, instance_data, pre_create_data): # metadata recollection as publish time. else: parenting_data = clip_instances[shot_creator_id] - sub_instance_data.update({ - "parent_instance_id": parenting_data["instance_id"], - "label": ( - f"{shot_folder_path} " - f"{creator.product_type}" - ), - "creator_attributes": { - "parentInstance": parenting_data["label"], + sub_instance_data.update( + { + "parent_instance_id": parenting_data["instance_id"], + "label": (f"{shot_folder_path} " f"{creator.product_type}"), + "creator_attributes": { + "parentInstance": parenting_data["label"] + }, } - }) + ) + + # add reviewable source to plate if shot has it + if sub_instance_data.get("reviewableSource"): + sub_instance_data["creator_attributes"].update({ + "reviewableSource": sub_instance_data[ + "reviewableSource"], + "review": True, + }) instance = creator.create(sub_instance_data, None) instance.transient_data["track_item"] = track_item @@ -642,7 +701,7 @@ def _collect_legacy_instance(self, track_item): "tag.handleStart": ("handleStart", int), "tag.handleEnd": ("handleEnd", int), "tag.folderPath": ("folderPath", str), - "tag.reviewTrack": ("reviewTrack", str), + "tag.reviewTrack": ("reviewableSource", str), "tag.variant": ("variant", str), "tag.workfileFrameStart": ("workfileFrameStart", int), "tag.sourceResolution": ("sourceResolution", bool), @@ -738,17 +797,21 @@ def _collect_legacy_instance(self, track_item): for sub_creator_id in sub_creators: sub_instance_data = instance_data.copy() creator = self.create_context.creators[sub_creator_id] - sub_instance_data.update({ - "clip_variant": sub_instance_data["variant"], - "parent_instance_id": parenting_data["instance_id"], - "label": ( - f"{sub_instance_data['folderPath']} " - f"{creator.product_type}" - ), - "creator_attributes": { - "parentInstance": parenting_data["label"], + sub_instance_data.update( + { + "clip_variant": sub_instance_data["variant"], + "parent_instance_id": parenting_data["instance_id"], + "label": ( + f"{sub_instance_data['folderPath']} " + f"{creator.product_type}" + ), + "creator_attributes": { + "parentInstance": parenting_data["label"], + "reviewableSource": sub_instance_data["reviewableSource"], + "review": False, + }, } - }) + ) instance = creator.create(sub_instance_data, None) instance.transient_data["track_item"] = track_item diff --git a/client/ayon_hiero/plugins/publish/collect_plates.py b/client/ayon_hiero/plugins/publish/collect_plates.py index 023a86e..fc8f487 100644 --- a/client/ayon_hiero/plugins/publish/collect_plates.py +++ b/client/ayon_hiero/plugins/publish/collect_plates.py @@ -16,6 +16,22 @@ def process(self, instance): """ instance.data["families"].append("clip") + # solve reviewable options + review_switch = instance.data["creator_attributes"].get( + "review") + reviewable_source = instance.data["creator_attributes"].get( + "reviewableSource") + + if review_switch is True: + if reviewable_source == "clip_media": + instance.data["families"].append("review") + else: + instance.data["reviewTrack"] = reviewable_source + + # remove review keys from instance data + instance.data.pop("reviewableSource", None) + instance.data.pop("review", None) + # Retrieve instance data from parent instance shot instance. parent_instance_id = instance.data["parent_instance_id"] edit_shared_data = instance.context.data["editorialSharedData"]