From c08713c258a230ab20494e692fce9eaa488b8cd3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Jul 2022 17:19:28 +0200 Subject: [PATCH] trayp: udpating editorial creator --- .../plugins/create/create_editorial.py | 220 ++++++++++++++++-- 1 file changed, 207 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 560a5ae047..ed91f0201f 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -1,5 +1,6 @@ import os from copy import deepcopy +from pprint import pformat import opentimelineio as otio from openpype.client import get_asset_by_name from openpype.hosts.traypublisher.api.plugin import ( @@ -23,6 +24,31 @@ from openpype.lib import ( from openpype.hosts.traypublisher.api.pipeline import HostContext +CLIP_ATTR_DEFS = [ + NumberDef( + "timeline_offset", + default=900000, + label="Timeline offset" + ), + UISeparatorDef(), + NumberDef( + "workfile_start_frame", + default=1001, + label="Workfile start frame" + ), + NumberDef( + "handle_start", + default=0, + label="Handle start" + ), + NumberDef( + "handle_end", + default=0, + label="Handle end" + ) +] + + class EditorialClipInstanceCreator(InvisibleTrayPublishCreator): identifier = "editorialClip" family = "clip" @@ -41,6 +67,32 @@ class EditorialClipInstanceCreator(InvisibleTrayPublishCreator): self.log.info(f"instance_data: {instance_data}") self.log.info(f"source_data: {source_data}") + instance_name = "{}_{}".format( + instance_data["name"], + "plateMain" + ) + return self._create_instance(instance_name, instance_data) + + def _create_instance(self, subset_name, data): + # Create new instance + new_instance = CreatedInstance(self.family, subset_name, data, self) + # Host implementation of storing metadata about instance + HostContext.add_instance(new_instance.data_to_store()) + # Add instance to current context + self._add_instance_to_context(new_instance) + + return new_instance + + def get_instance_attr_defs(self): + attr_defs = [ + TextDef( + "asset_name", + label="Asset name", + ) + ] + attr_defs.extend(CLIP_ATTR_DEFS) + return attr_defs + class EditorialSimpleCreator(TrayPublishCreator): @@ -57,6 +109,8 @@ or updating already created. Publishing will create OTIO file. """ icon = "fa.file" + + def __init__( self, project_settings, *args, **kwargs ): @@ -74,19 +128,29 @@ or updating already created. Publishing will create OTIO file. self.default_variants = self._creator_settings["default_variants"] def create(self, subset_name, instance_data, pre_create_data): + clip_instance_properties = { + k: v for k, v in pre_create_data.items() + if k != "sequence_filepath_data" + } # TODO: create otio instance asset_name = instance_data["asset"] asset_doc = get_asset_by_name(self.project_name, asset_name) + fps = asset_doc["data"]["fps"] + instance_data.update({ + "fps": fps + }) otio_timeline = self._create_otio_instance( subset_name, instance_data, pre_create_data) # TODO: create clip instances - editorial_clip_creator = self.create_context.creators["editorialClip"] - editorial_clip_creator.create({}, {}) + clip_instance_properties.update({ + "fps": fps, + "asset_name": asset_name + }) + self._get_clip_instances( + asset_name, otio_timeline, clip_instance_properties) def _create_otio_instance(self, subset_name, data, pre_create_data): - # from openpype import lib as plib - # get path of sequence file_path_data = pre_create_data["sequence_filepath_data"] file_path = os.path.join( @@ -100,8 +164,7 @@ or updating already created. Publishing will create OTIO file. if extension == ".edl": # EDL has no frame rate embedded so needs explicit # frame rate else 24 is asssumed. - kwargs["rate"] = float(25) - # plib.get_asset()["data"]["fps"] + kwargs["rate"] = data["fps"] self.log.info(f"kwargs: {kwargs}") otio_timeline = otio.adapters.read_from_file( @@ -109,15 +172,144 @@ or updating already created. Publishing will create OTIO file. # Pass precreate data to creator attributes data.update({ - "creator_attributes": pre_create_data, - "editorial_creator": True - + "sequence_file_path": file_path }) self._create_instance(self.family, subset_name, data) return otio_timeline + def _get_clip_instances( + self, + asset_name, + otio_timeline, + clip_instance_properties + ): + parent_asset_name = clip_instance_properties["asset_name"] + handle_start = clip_instance_properties["handle_start"] + handle_end = clip_instance_properties["handle_end"] + timeline_offset = clip_instance_properties["timeline_offset"] + workfile_start_frame = clip_instance_properties["workfile_start_frame"] + fps = clip_instance_properties["fps"] + + assets_shared = {} + self.asset_name_check = [] + + editorial_clip_creator = self.create_context.creators["editorialClip"] + + tracks = otio_timeline.each_child( + descended_from_type=otio.schema.Track + ) + + for track in tracks: + self.log.debug(f"track.name: {track.name}") + try: + track_start_frame = ( + abs(track.source_range.start_time.value) + ) + self.log.debug(f"track_start_frame: {track_start_frame}") + track_start_frame -= self.timeline_frame_start + except AttributeError: + track_start_frame = 0 + + self.log.debug(f"track_start_frame: {track_start_frame}") + + for clip in track.each_child(): + + if not self._validate_clip_for_processing(clip): + continue + + # basic unique asset name + clip_name = os.path.splitext(clip.name)[0].lower() + name = f"{asset_name.split('_')[0]}_{clip_name}" + + # make sure the name is unique + self._validate_name_uniqueness(name) + + # frame ranges data + clip_in = clip.range_in_parent().start_time.value + clip_in += track_start_frame + clip_out = clip.range_in_parent().end_time_inclusive().value + clip_out += track_start_frame + self.log.info(f"clip_in: {clip_in} | clip_out: {clip_out}") + + # add offset in case there is any + if timeline_offset: + clip_in += timeline_offset + clip_out += timeline_offset + + clip_duration = clip.duration().value + self.log.info(f"clip duration: {clip_duration}") + + source_in = clip.trimmed_range().start_time.value + source_out = source_in + clip_duration + + # define starting frame for future shot + frame_start = ( + clip_in if workfile_start_frame is None + else workfile_start_frame + ) + frame_end = frame_start + (clip_duration - 1) + + # create shared new instance data + instance_data = { + "variant": "Main", + "families": ["plate"], + + # shared attributes + "asset": parent_asset_name, + "name": clip_name, + "task": "Compositing", + + # parent time properties + "trackStartFrame": track_start_frame, + + # creator_attributes + "creator_attributes": { + "asset_name": clip_name, + "timeline_offset": timeline_offset, + "workfile_start_frame": workfile_start_frame, + "frameStart": frame_start, + "frameEnd": frame_end, + "fps": fps, + "handle_start": handle_start, + "handle_end": handle_end, + "clipIn": clip_in, + "clipOut": clip_out, + "sourceIn": source_in, + "sourceOut": source_out, + } + } + + c_instance = editorial_clip_creator.create(instance_data, {}) + self.log.debug(f"{pformat(dict(c_instance.data))}") + + def _validate_clip_for_processing(self, clip): + if clip.name is None: + return False + + if isinstance(clip, otio.schema.Gap): + return False + + # skip all generators like black empty + if isinstance( + clip.media_reference, + otio.schema.GeneratorReference): + return False + + # Transitions are ignored, because Clips have the full frame + # range. + if isinstance(clip, otio.schema.Transition): + return False + + return True + + def _validate_name_uniqueness(self, name): + if name not in self.asset_name_check: + self.asset_name_check.append(name) + else: + self.log.warning(f"duplicate shot name: {name}") + def _create_instance(self, family, subset_name, data): # Create new instance new_instance = CreatedInstance(family, subset_name, data, self) @@ -128,7 +320,7 @@ or updating already created. Publishing will create OTIO file. def get_pre_create_attr_defs(self): # Use same attributes as for instance attrobites - return [ + attr_defs = [ FileDef( "sequence_filepath_data", folders=False, @@ -141,6 +333,8 @@ or updating already created. Publishing will create OTIO file. allow_sequences=False, label="Filepath", ), - UISeparatorDef(), - UILabelDef("Clip instance attributes") - ] \ No newline at end of file + UILabelDef("Clip instance attributes"), + UISeparatorDef() + ] + attr_defs.extend(CLIP_ATTR_DEFS) + return attr_defs