diff --git a/pype/hosts/resolve/__init__.py b/pype/hosts/resolve/__init__.py index 5f651f4b29..c8f45259ff 100644 --- a/pype/hosts/resolve/__init__.py +++ b/pype/hosts/resolve/__init__.py @@ -20,6 +20,8 @@ from .lib import ( get_current_track_items, create_current_sequence_media_bin, create_compound_clip, + swap_clips, + get_pype_clip_metadata, set_project_manager_to_folder_name ) @@ -61,6 +63,8 @@ __all__ = [ "get_current_track_items", "create_current_sequence_media_bin", "create_compound_clip", + "swap_clips", + "get_pype_clip_metadata", "set_project_manager_to_folder_name", # menu diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index 50b70241c0..db3bd989bf 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -1,6 +1,7 @@ import sys import json from opentimelineio import opentime +from pprint import pformat from pype.api import Logger @@ -10,6 +11,7 @@ self = sys.modules[__name__] self.pm = None self.rename_index = 0 self.rename_add = 0 +self.pype_metadata_key = "VFX Notes" def get_project_manager(): @@ -46,11 +48,11 @@ def get_current_track_items( selected_clips = list() # get all tracks count filtered by track type - sequence_video_count = sequence.GetTrackCount(track_type) + selected_track_count = sequence.GetTrackCount(track_type) # loop all tracks and get items _clips = dict() - for track_index in range(1, (int(sequence_video_count) + 1)): + for track_index in range(1, (int(selected_track_count) + 1)): track_name = sequence.GetTrackName(track_type, track_index) track_track_items = sequence.GetItemListInTrack( track_type, track_index) @@ -190,7 +192,6 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): Returns: resolve.MediaPoolItem: media pool item with compound clip timeline(cct) """ - # get basic objects form data project = clip_data["project"] sequence = clip_data["sequence"] @@ -204,6 +205,7 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): # get clip attributes clip_attributes = get_clip_attributes(clip_item) + print(f"_ clip_attributes: {pformat(clip_attributes)}") if rename: presets = kwargs.get("presets") @@ -281,9 +283,11 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): project.SetCurrentTimeline(sq_origin) # Add collected metadata and attributes to the comound clip: - if clip_attributes.get("VFX Notes"): - clip_attributes["VFX Notes"] = mp_item.GetMetadata( - "VFX Notes")["VFX Notes"] + if mp_item.GetMetadata(self.pype_metadata_key): + clip_attributes[self.pype_metadata_key] = mp_item.GetMetadata( + self.pype_metadata_key)[self.pype_metadata_key] + + # stringify clip_attributes = json.dumps(clip_attributes) # add attributes to metadata @@ -291,7 +295,7 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): cct.SetMetadata(k, v) # add metadata to cct - cct.SetMetadata("VFX Notes", clip_attributes) + cct.SetMetadata(self.pype_metadata_key, clip_attributes) # reset start timecode of the compound clip cct.SetClipProperty("Start TC", mp_props["Start TC"]) @@ -356,6 +360,22 @@ def validate_tc(x): print('Invalid timecode. Try again.') +def get_pype_clip_metadata(clip): + """ + Get pype metadata created by creator plugin + + Attributes: + clip (resolve.TimelineItem): resolve's object + + Returns: + dict: hierarchy, orig clip attributes + """ + mp_item = clip.GetMediaPoolItem() + metadata = mp_item.GetMetadata() + + return metadata.get(self.pype_metadata_key) + + def get_clip_attributes(clip): """ Collect basic atrributes from resolve timeline item diff --git a/pype/plugins/resolve/publish/collect_clips.py b/pype/plugins/resolve/publish/collect_clips.py new file mode 100644 index 0000000000..0f02f26f2e --- /dev/null +++ b/pype/plugins/resolve/publish/collect_clips.py @@ -0,0 +1,159 @@ +import os +from pyblish import api +from pype.hosts import resolve +import json + +class CollectClips(api.ContextPlugin): + """Collect all Track items selection.""" + + order = api.CollectorOrder + 0.01 + label = "Collect Clips" + hosts = ["resolve"] + + def process(self, context): + # create asset_names conversion table + if not context.data.get("assetsShared"): + self.log.debug("Created `assetsShared` in context") + context.data["assetsShared"] = dict() + + projectdata = context.data["projectEntity"]["data"] + selection = resolve.get_current_track_items( + filter=True, selecting_color="Pink") + + for clip_data in selection: + data = dict() + + # get basic objects form data + project = clip_data["project"] + sequence = clip_data["sequence"] + clip = clip_data["clip"] + + # sequence attrs + sq_frame_start = sequence.GetStartFrame() + self.log.debug(f"sq_frame_start: {sq_frame_start}") + + sq_markers = sequence.GetMarkers() + + # get details of objects + clip_item = clip["item"] + track = clip_data["track"] + + mp = project.GetMediaPool() + + # get clip attributes + clip_metadata = resolve.get_pype_clip_metadata(clip_item) + clip_metadata = json.loads(clip_metadata) + self.log.debug(f"clip_metadata: {clip_metadata}") + + compound_source_prop = clip_metadata["sourceProperties"] + self.log.debug(f"compound_source_prop: {compound_source_prop}") + + asset_name = clip_item.GetName() + mp_item = clip_item.GetMediaPoolItem() + mp_prop = mp_item.GetClipProperty() + source_first = int(compound_source_prop["Start"]) + source_last = int(compound_source_prop["End"]) + source_duration = compound_source_prop["Frames"] + fps = float(mp_prop["FPS"]) + self.log.debug(f"source_first: {source_first}") + self.log.debug(f"source_last: {source_last}") + self.log.debug(f"source_duration: {source_duration}") + self.log.debug(f"fps: {fps}") + + source_path = os.path.normpath( + compound_source_prop["File Path"]) + source_name = compound_source_prop["File Name"] + source_id = clip_metadata["sourceId"] + self.log.debug(f"source_path: {source_path}") + self.log.debug(f"source_name: {source_name}") + self.log.debug(f"source_id: {source_id}") + + clip_left_offset = int(clip_item.GetLeftOffset()) + clip_right_offset = int(clip_item.GetRightOffset()) + self.log.debug(f"clip_left_offset: {clip_left_offset}") + self.log.debug(f"clip_right_offset: {clip_right_offset}") + + # source in/out + source_in = int(source_first + clip_left_offset) + source_out = int(source_first + clip_right_offset) + self.log.debug(f"source_in: {source_in}") + self.log.debug(f"source_out: {source_out}") + + clip_in = int(clip_item.GetStart() - sq_frame_start) + clip_out = int(clip_item.GetEnd() - sq_frame_start) + clip_duration = int(clip_item.GetDuration()) + self.log.debug(f"clip_in: {clip_in}") + self.log.debug(f"clip_out: {clip_out}") + self.log.debug(f"clip_duration: {clip_duration}") + + is_sequence = False + + self.log.debug( + "__ assets_shared: {}".format( + context.data["assetsShared"])) + + # Check for clips with the same range + # this is for testing if any vertically neighbouring + # clips has been already processed + clip_matching_with_range = next( + (k for k, v in context.data["assetsShared"].items() + if (v.get("_clipIn", 0) == clip_in) + and (v.get("_clipOut", 0) == clip_out) + ), False) + + # check if clip name is the same in matched + # vertically neighbouring clip + # if it is then it is correct and resent variable to False + # not to be rised wrong name exception + if asset_name in str(clip_matching_with_range): + clip_matching_with_range = False + + # rise wrong name exception if found one + assert (not clip_matching_with_range), ( + "matching clip: {asset}" + " timeline range ({clip_in}:{clip_out})" + " conflicting with {clip_matching_with_range}" + " >> rename any of clips to be the same as the other <<" + ).format( + **locals()) + + if ("[" in source_name) and ("]" in source_name): + is_sequence = True + + data.update({ + "name": "_".join([ + track["name"], asset_name, source_name]), + "item": clip_item, + "source": mp_item, + # "timecodeStart": str(source.timecodeStart()), + "timelineStart": sq_frame_start, + "sourcePath": source_path, + "sourceFileHead": source_name, + "isSequence": is_sequence, + "track": track["name"], + "trackIndex": track["index"], + "sourceFirst": source_first, + + "sourceIn": source_in, + "sourceOut": source_out, + "mediaDuration": source_duration, + "clipIn": clip_in, + "clipOut": clip_out, + "clipDuration": clip_duration, + "asset": asset_name, + "subset": "plateMain", + "family": "clip", + "families": [], + "handleStart": projectdata.get("handleStart", 0), + "handleEnd": projectdata.get("handleEnd", 0)}) + + instance = context.create_instance(**data) + + self.log.info("Created instance: {}".format(instance)) + self.log.info("Created instance.data: {}".format(instance.data)) + + context.data["assetsShared"][asset_name] = { + "_clipIn": clip_in, + "_clipOut": clip_out + } + self.log.info("context.data[\"assetsShared\"]: {}".format(context.data["assetsShared"]))