diff --git a/openpype/hosts/flame/__init__.py b/openpype/hosts/flame/__init__.py index 48e8dc86c9..da28170679 100644 --- a/openpype/hosts/flame/__init__.py +++ b/openpype/hosts/flame/__init__.py @@ -19,7 +19,7 @@ from .api.lib import ( maintain_current_timeline, get_project_manager, get_current_project, - get_current_timeline, + get_current_sequence, create_bin, ) @@ -51,6 +51,7 @@ INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") app_framework = None apps = [] +selection = None __all__ = [ @@ -65,6 +66,7 @@ __all__ = [ "app_framework", "apps", + "selection", # pipeline "install", @@ -86,7 +88,7 @@ __all__ = [ "maintain_current_timeline", "get_project_manager", "get_current_project", - "get_current_timeline", + "get_current_sequence", "create_bin", # menu diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 89e020b329..96bffab774 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -220,10 +220,10 @@ def maintain_current_timeline(to_timeline, from_timeline=None): timeline2 >>> with maintain_current_timeline(to_timeline): - ... print(get_current_timeline().GetName()) + ... print(get_current_sequence().GetName()) timeline2 - >>> print(get_current_timeline().GetName()) + >>> print(get_current_sequence().GetName()) timeline1 """ # todo: this is still Resolve's implementation @@ -256,9 +256,28 @@ def get_current_project(): return -def get_current_timeline(new=False): - # TODO: get_current_timeline - return +def get_current_sequence(selection): + import flame + + def segment_to_sequence(_segment): + track = _segment.parent + version = track.parent + return version.parent + + process_timeline = None + + if len(selection) == 1: + if isinstance(selection[0], flame.PySequence): + process_timeline = selection[0] + if isinstance(selection[0], flame.PySegment): + process_timeline = segment_to_sequence(selection[0]) + else: + for segment in selection: + if isinstance(segment, flame.PySegment): + process_timeline = segment_to_sequence(segment) + break + + return process_timeline def create_bin(name, root=None): @@ -272,3 +291,46 @@ def rescan_hooks(): flame.execute_shortcut('Rescan Python Hooks') except Exception: pass + + +def get_metadata(project_name, _log=None): + from adsk.libwiretapPythonClientAPI import ( + WireTapClient, + WireTapServerHandle, + WireTapNodeHandle, + WireTapStr + ) + + class GetProjectColorPolicy(object): + def __init__(self, host_name=None, _log=None): + # Create a connection to the Backburner manager using the Wiretap + # python API. + # + self.log = _log or log + self.host_name = host_name or "localhost" + self._wiretap_client = WireTapClient() + if not self._wiretap_client.init(): + raise Exception("Could not initialize Wiretap Client") + self._server = WireTapServerHandle( + "{}:IFFFS".format(self.host_name)) + + def process(self, project_name): + policy_node_handle = WireTapNodeHandle( + self._server, + "/projects/{}/syncolor/policy".format(project_name) + ) + self.log.info(policy_node_handle) + + policy = WireTapStr() + if not policy_node_handle.getNodeTypeStr(policy): + self.log.warning( + "Could not retrieve policy of '%s': %s" % ( + policy_node_handle.getNodeId().id(), + policy_node_handle.lastError() + ) + ) + + return policy.c_str() + + policy_wiretap = GetProjectColorPolicy(_log=_log) + return policy_wiretap.process(project_name) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index b4f1728acf..fef6dbfa35 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -4,7 +4,6 @@ from copy import deepcopy from openpype.tools.utils.host_tools import HostToolsHelper - menu_group_name = 'OpenPype' default_flame_export_presets = { @@ -26,6 +25,13 @@ default_flame_export_presets = { } +def callback_selection(selection, function): + import openpype.hosts.flame as opflame + opflame.selection = selection + print(opflame.selection) + function() + + class _FlameMenuApp(object): def __init__(self, framework): self.name = self.__class__.__name__ @@ -97,9 +103,6 @@ class FlameMenuProjectConnect(_FlameMenuApp): if not self.flame: return [] - flame_project_name = self.flame_project_name - self.log.info("______ {} ______".format(flame_project_name)) - menu = deepcopy(self.menu) menu['actions'].append({ @@ -108,11 +111,13 @@ class FlameMenuProjectConnect(_FlameMenuApp): }) menu['actions'].append({ "name": "Create ...", - "execute": lambda x: self.tools_helper.show_creator() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_creator) }) menu['actions'].append({ "name": "Publish ...", - "execute": lambda x: self.tools_helper.show_publish() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_publish) }) menu['actions'].append({ "name": "Load ...", @@ -128,9 +133,6 @@ class FlameMenuProjectConnect(_FlameMenuApp): }) return menu - def get_projects(self, *args, **kwargs): - pass - def refresh(self, *args, **kwargs): self.rescan() @@ -165,18 +167,17 @@ class FlameMenuTimeline(_FlameMenuApp): if not self.flame: return [] - flame_project_name = self.flame_project_name - self.log.info("______ {} ______".format(flame_project_name)) - menu = deepcopy(self.menu) menu['actions'].append({ "name": "Create ...", - "execute": lambda x: self.tools_helper.show_creator() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_creator) }) menu['actions'].append({ "name": "Publish ...", - "execute": lambda x: self.tools_helper.show_publish() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_publish) }) menu['actions'].append({ "name": "Load ...", @@ -189,9 +190,6 @@ class FlameMenuTimeline(_FlameMenuApp): return menu - def get_projects(self, *args, **kwargs): - pass - def refresh(self, *args, **kwargs): self.rescan() diff --git a/openpype/hosts/flame/otio/__init__.py b/openpype/hosts/flame/otio/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py new file mode 100644 index 0000000000..aea1f387e8 --- /dev/null +++ b/openpype/hosts/flame/otio/flame_export.py @@ -0,0 +1,657 @@ +""" compatibility OpenTimelineIO 0.12.0 and newer +""" + +import os +import re +import json +import logging +import opentimelineio as otio +from . import utils + +import flame +from pprint import pformat + +reload(utils) # noqa + +log = logging.getLogger(__name__) + + +TRACK_TYPES = { + "video": otio.schema.TrackKind.Video, + "audio": otio.schema.TrackKind.Audio +} +MARKERS_COLOR_MAP = { + (1.0, 0.0, 0.0): otio.schema.MarkerColor.RED, + (1.0, 0.5, 0.0): otio.schema.MarkerColor.ORANGE, + (1.0, 1.0, 0.0): otio.schema.MarkerColor.YELLOW, + (1.0, 0.5, 1.0): otio.schema.MarkerColor.PINK, + (1.0, 1.0, 1.0): otio.schema.MarkerColor.WHITE, + (0.0, 1.0, 0.0): otio.schema.MarkerColor.GREEN, + (0.0, 1.0, 1.0): otio.schema.MarkerColor.CYAN, + (0.0, 0.0, 1.0): otio.schema.MarkerColor.BLUE, + (0.5, 0.0, 0.5): otio.schema.MarkerColor.PURPLE, + (0.5, 0.0, 1.0): otio.schema.MarkerColor.MAGENTA, + (0.0, 0.0, 0.0): otio.schema.MarkerColor.BLACK +} +MARKERS_INCLUDE = True + + +class CTX: + _fps = None + _tl_start_frame = None + project = None + clips = None + + @classmethod + def set_fps(cls, new_fps): + if not isinstance(new_fps, float): + raise TypeError("Invalid fps type {}".format(type(new_fps))) + if cls._fps != new_fps: + cls._fps = new_fps + + @classmethod + def get_fps(cls): + return cls._fps + + @classmethod + def set_tl_start_frame(cls, number): + if not isinstance(number, int): + raise TypeError("Invalid timeline start frame type {}".format( + type(number))) + if cls._tl_start_frame != number: + cls._tl_start_frame = number + + @classmethod + def get_tl_start_frame(cls): + return cls._tl_start_frame + + +def flatten(_list): + for item in _list: + if isinstance(item, (list, tuple)): + for sub_item in flatten(item): + yield sub_item + else: + yield item + + +def get_current_flame_project(): + project = flame.project.current_project + return project + + +def create_otio_rational_time(frame, fps): + return otio.opentime.RationalTime( + float(frame), + float(fps) + ) + + +def create_otio_time_range(start_frame, frame_duration, fps): + return otio.opentime.TimeRange( + start_time=create_otio_rational_time(start_frame, fps), + duration=create_otio_rational_time(frame_duration, fps) + ) + + +def _get_metadata(item): + if hasattr(item, 'metadata'): + if not item.metadata: + return {} + return {key: value for key, value in dict(item.metadata)} + return {} + + +def create_time_effects(otio_clip, item): + # todo #2426: add retiming effects to export + # get all subtrack items + # subTrackItems = flatten(track_item.parent().subTrackItems()) + # speed = track_item.playbackSpeed() + + # otio_effect = None + # # retime on track item + # if speed != 1.: + # # make effect + # otio_effect = otio.schema.LinearTimeWarp() + # otio_effect.name = "Speed" + # otio_effect.time_scalar = speed + # otio_effect.metadata = {} + + # # freeze frame effect + # if speed == 0.: + # otio_effect = otio.schema.FreezeFrame() + # otio_effect.name = "FreezeFrame" + # otio_effect.metadata = {} + + # if otio_effect: + # # add otio effect to clip effects + # otio_clip.effects.append(otio_effect) + + # # loop trought and get all Timewarps + # for effect in subTrackItems: + # if ((track_item not in effect.linkedItems()) + # and (len(effect.linkedItems()) > 0)): + # continue + # # avoid all effect which are not TimeWarp and disabled + # if "TimeWarp" not in effect.name(): + # continue + + # if not effect.isEnabled(): + # continue + + # node = effect.node() + # name = node["name"].value() + + # # solve effect class as effect name + # _name = effect.name() + # if "_" in _name: + # effect_name = re.sub(r"(?:_)[_0-9]+", "", _name) # more numbers + # else: + # effect_name = re.sub(r"\d+", "", _name) # one number + + # metadata = {} + # # add knob to metadata + # for knob in ["lookup", "length"]: + # value = node[knob].value() + # animated = node[knob].isAnimated() + # if animated: + # value = [ + # ((node[knob].getValueAt(i)) - i) + # for i in range( + # track_item.timelineIn(), + # track_item.timelineOut() + 1) + # ] + + # metadata[knob] = value + + # # make effect + # otio_effect = otio.schema.TimeEffect() + # otio_effect.name = name + # otio_effect.effect_name = effect_name + # otio_effect.metadata = metadata + + # # add otio effect to clip effects + # otio_clip.effects.append(otio_effect) + pass + + +def _get_marker_color(flame_colour): + # clamp colors to closes half numbers + _flame_colour = [ + (lambda x: round(x * 2) / 2)(c) + for c in flame_colour] + + for color, otio_color_type in MARKERS_COLOR_MAP.items(): + if _flame_colour == list(color): + return otio_color_type + + return otio.schema.MarkerColor.RED + + +def _get_flame_markers(item): + output_markers = [] + + time_in = item.record_in.relative_frame + + for marker in item.markers: + log.debug(marker) + start_frame = marker.location.get_value().relative_frame + + start_frame = (start_frame - time_in) + 1 + + marker_data = { + "name": marker.name.get_value(), + "duration": marker.duration.get_value().relative_frame, + "comment": marker.comment.get_value(), + "start_frame": start_frame, + "colour": marker.colour.get_value() + } + + output_markers.append(marker_data) + + return output_markers + + +def create_otio_markers(otio_item, item): + markers = _get_flame_markers(item) + for marker in markers: + frame_rate = CTX.get_fps() + + marked_range = otio.opentime.TimeRange( + start_time=otio.opentime.RationalTime( + marker["start_frame"], + frame_rate + ), + duration=otio.opentime.RationalTime( + marker["duration"], + frame_rate + ) + ) + + # testing the comment if it is not containing json string + check_if_json = re.findall( + re.compile(r"[{:}]"), + marker["comment"] + ) + + # to identify this as json, at least 3 items in the list should + # be present ["{", ":", "}"] + metadata = {} + if len(check_if_json) >= 3: + # this is json string + try: + # capture exceptions which are related to strings only + metadata.update( + json.loads(marker["comment"]) + ) + except ValueError as msg: + log.error("Marker json conversion: {}".format(msg)) + else: + metadata["comment"] = marker["comment"] + + otio_marker = otio.schema.Marker( + name=marker["name"], + color=_get_marker_color( + marker["colour"]), + marked_range=marked_range, + metadata=metadata + ) + + otio_item.markers.append(otio_marker) + + +def create_otio_reference(clip_data): + metadata = _get_metadata(clip_data) + + # get file info for path and start frame + frame_start = 0 + fps = CTX.get_fps() + + path = clip_data["fpath"] + + reel_clip = None + match_reel_clip = [ + clip for clip in CTX.clips + if clip["fpath"] == path + ] + if match_reel_clip: + reel_clip = match_reel_clip.pop() + fps = reel_clip["fps"] + + file_name = os.path.basename(path) + file_head, extension = os.path.splitext(file_name) + + # get padding and other file infos + log.debug("_ path: {}".format(path)) + + is_sequence = padding = utils.get_frame_from_path(path) + if is_sequence: + number = utils.get_frame_from_path(path) + file_head = file_name.split(number)[:-1] + frame_start = int(number) + + frame_duration = clip_data["source_duration"] + + if is_sequence: + metadata.update({ + "isSequence": True, + "padding": padding + }) + + otio_ex_ref_item = None + + if is_sequence: + # if it is file sequence try to create `ImageSequenceReference` + # the OTIO might not be compatible so return nothing and do it old way + try: + dirname = os.path.dirname(path) + otio_ex_ref_item = otio.schema.ImageSequenceReference( + target_url_base=dirname + os.sep, + name_prefix=file_head, + name_suffix=extension, + start_frame=frame_start, + frame_zero_padding=padding, + rate=fps, + available_range=create_otio_time_range( + frame_start, + frame_duration, + fps + ) + ) + except AttributeError: + pass + + if not otio_ex_ref_item: + reformat_path = utils.get_reformated_path(path, padded=False) + # in case old OTIO or video file create `ExternalReference` + otio_ex_ref_item = otio.schema.ExternalReference( + target_url=reformat_path, + available_range=create_otio_time_range( + frame_start, + frame_duration, + fps + ) + ) + + # add metadata to otio item + add_otio_metadata(otio_ex_ref_item, clip_data, **metadata) + + return otio_ex_ref_item + + +def create_otio_clip(clip_data): + segment = clip_data["PySegment"] + + # create media reference + media_reference = create_otio_reference(clip_data) + + # calculate source in + first_frame = utils.get_frame_from_path(clip_data["fpath"]) or 0 + source_in = int(clip_data["source_in"]) - int(first_frame) + + # creatae source range + source_range = create_otio_time_range( + source_in, + clip_data["record_duration"], + CTX.get_fps() + ) + + otio_clip = otio.schema.Clip( + name=clip_data["segment_name"], + source_range=source_range, + media_reference=media_reference + ) + + # Add markers + if MARKERS_INCLUDE: + create_otio_markers(otio_clip, segment) + + return otio_clip + + +def create_otio_gap(gap_start, clip_start, tl_start_frame, fps): + return otio.schema.Gap( + source_range=create_otio_time_range( + gap_start, + (clip_start - tl_start_frame) - gap_start, + fps + ) + ) + + +def get_clips_in_reels(project): + output_clips = [] + project_desktop = project.current_workspace.desktop + + for reel_group in project_desktop.reel_groups: + for reel in reel_group.reels: + for clip in reel.clips: + clip_data = { + "PyClip": clip, + "fps": float(str(clip.frame_rate)[:-4]) + } + + attrs = [ + "name", "width", "height", + "ratio", "sample_rate", "bit_depth" + ] + + for attr in attrs: + val = getattr(clip, attr) + clip_data[attr] = val + + version = clip.versions[-1] + track = version.tracks[-1] + for segment in track.segments: + segment_data = _get_segment_attributes(segment) + clip_data.update(segment_data) + + output_clips.append(clip_data) + + return output_clips + + +def _get_colourspace_policy(): + + output = {} + # get policies project path + policy_dir = "/opt/Autodesk/project/{}/synColor/policy".format( + CTX.project.name + ) + log.debug(policy_dir) + policy_fp = os.path.join(policy_dir, "policy.cfg") + + if not os.path.exists(policy_fp): + return output + + with open(policy_fp) as file: + dict_conf = dict(line.strip().split(' = ', 1) for line in file) + output.update( + {"openpype.flame.{}".format(k): v for k, v in dict_conf.items()} + ) + return output + + +def _create_otio_timeline(sequence): + + metadata = _get_metadata(sequence) + + # find colour policy files and add them to metadata + colorspace_policy = _get_colourspace_policy() + metadata.update(colorspace_policy) + + metadata.update({ + "openpype.timeline.width": int(sequence.width), + "openpype.timeline.height": int(sequence.height), + "openpype.timeline.pixelAspect": 1 + }) + + rt_start_time = create_otio_rational_time( + CTX.get_tl_start_frame(), CTX.get_fps()) + + return otio.schema.Timeline( + name=str(sequence.name)[1:-1], + global_start_time=rt_start_time, + metadata=metadata + ) + + +def create_otio_track(track_type, track_name): + return otio.schema.Track( + name=track_name, + kind=TRACK_TYPES[track_type] + ) + + +def add_otio_gap(clip_data, otio_track, prev_out): + gap_length = clip_data["record_in"] - prev_out + if prev_out != 0: + gap_length -= 1 + + gap = otio.opentime.TimeRange( + duration=otio.opentime.RationalTime( + gap_length, + CTX.get_fps() + ) + ) + otio_gap = otio.schema.Gap(source_range=gap) + otio_track.append(otio_gap) + + +def add_otio_metadata(otio_item, item, **kwargs): + metadata = _get_metadata(item) + + # add additional metadata from kwargs + if kwargs: + metadata.update(kwargs) + + # add metadata to otio item metadata + for key, value in metadata.items(): + otio_item.metadata.update({key: value}) + + +def _get_shot_tokens_values(clip, tokens): + old_value = None + output = {} + + if not clip.shot_name: + return output + + old_value = clip.shot_name.get_value() + + for token in tokens: + clip.shot_name.set_value(token) + _key = re.sub("[ <>]", "", token) + + try: + output[_key] = int(clip.shot_name.get_value()) + except ValueError: + output[_key] = clip.shot_name.get_value() + + clip.shot_name.set_value(old_value) + + return output + + +def _get_segment_attributes(segment): + # log.debug(dir(segment)) + + if str(segment.name)[1:-1] == "": + return None + + # Add timeline segment to tree + clip_data = { + "segment_name": segment.name.get_value(), + "segment_comment": segment.comment.get_value(), + "tape_name": segment.tape_name, + "source_name": segment.source_name, + "fpath": segment.file_path, + "PySegment": segment + } + + # add all available shot tokens + shot_tokens = _get_shot_tokens_values(segment, [ + "", "", "", "", + ]) + clip_data.update(shot_tokens) + + # populate shot source metadata + segment_attrs = [ + "record_duration", "record_in", "record_out", + "source_duration", "source_in", "source_out" + ] + segment_attrs_data = {} + for attr in segment_attrs: + if not hasattr(segment, attr): + continue + _value = getattr(segment, attr) + segment_attrs_data[attr] = str(_value).replace("+", ":") + + if attr in ["record_in", "record_out"]: + clip_data[attr] = _value.relative_frame + else: + clip_data[attr] = _value.frame + + clip_data["segment_timecodes"] = segment_attrs_data + + return clip_data + + +def create_otio_timeline(sequence): + log.info(dir(sequence)) + log.info(sequence.attributes) + + CTX.project = get_current_flame_project() + CTX.clips = get_clips_in_reels(CTX.project) + + log.debug(pformat( + CTX.clips + )) + + # get current timeline + CTX.set_fps( + float(str(sequence.frame_rate)[:-4])) + + tl_start_frame = utils.timecode_to_frames( + str(sequence.start_time).replace("+", ":"), + CTX.get_fps() + ) + CTX.set_tl_start_frame(tl_start_frame) + + # convert timeline to otio + otio_timeline = _create_otio_timeline(sequence) + + # create otio tracks and clips + for ver in sequence.versions: + for track in ver.tracks: + if len(track.segments) == 0 and track.hidden: + return None + + # convert track to otio + otio_track = create_otio_track( + "video", str(track.name)[1:-1]) + + all_segments = [] + for segment in track.segments: + clip_data = _get_segment_attributes(segment) + if not clip_data: + continue + all_segments.append(clip_data) + + segments_ordered = { + itemindex: clip_data + for itemindex, clip_data in enumerate( + all_segments) + } + log.debug("_ segments_ordered: {}".format( + pformat(segments_ordered) + )) + if not segments_ordered: + continue + + for itemindex, segment_data in segments_ordered.items(): + log.debug("_ itemindex: {}".format(itemindex)) + + # Add Gap if needed + if itemindex == 0: + # if it is first track item at track then add + # it to previouse item + prev_item = segment_data + + else: + # get previouse item + prev_item = segments_ordered[itemindex - 1] + + log.debug("_ segment_data: {}".format(segment_data)) + + # calculate clip frame range difference from each other + clip_diff = segment_data["record_in"] - prev_item["record_out"] + + # add gap if first track item is not starting + # at first timeline frame + if itemindex == 0 and segment_data["record_in"] > 0: + add_otio_gap(segment_data, otio_track, 0) + + # or add gap if following track items are having + # frame range differences from each other + elif itemindex and clip_diff != 1: + add_otio_gap( + segment_data, otio_track, prev_item["record_out"]) + + # create otio clip and add it to track + otio_clip = create_otio_clip(segment_data) + otio_track.append(otio_clip) + + log.debug("_ otio_clip: {}".format(otio_clip)) + + # create otio marker + # create otio metadata + + # add track to otio timeline + otio_timeline.tracks.append(otio_track) + + return otio_timeline + + +def write_to_file(otio_timeline, path): + otio.adapters.write_to_file(otio_timeline, path) diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py new file mode 100644 index 0000000000..229946343b --- /dev/null +++ b/openpype/hosts/flame/otio/utils.py @@ -0,0 +1,95 @@ +import re +import opentimelineio as otio +import logging +log = logging.getLogger(__name__) + + +def timecode_to_frames(timecode, framerate): + rt = otio.opentime.from_timecode(timecode, framerate) + return int(otio.opentime.to_frames(rt)) + + +def frames_to_timecode(frames, framerate): + rt = otio.opentime.from_frames(frames, framerate) + return otio.opentime.to_timecode(rt) + + +def frames_to_seconds(frames, framerate): + rt = otio.opentime.from_frames(frames, framerate) + return otio.opentime.to_seconds(rt) + + +def get_reformated_path(path, padded=True): + """ + Return fixed python expression path + + Args: + path (str): path url or simple file name + + Returns: + type: string with reformated path + + Example: + get_reformated_path("plate.1001.exr") > plate.%04d.exr + + """ + padding = get_padding_from_path(path) + found = get_frame_from_path(path) + + if not found: + log.info("Path is not sequence: {}".format(path)) + return path + + if padded: + path = path.replace(found, "%0{}d".format(padding)) + else: + path = path.replace(found, "%d") + + return path + + +def get_padding_from_path(path): + """ + Return padding number from Flame path style + + Args: + path (str): path url or simple file name + + Returns: + int: padding number + + Example: + get_padding_from_path("plate.0001.exr") > 4 + + """ + found = get_frame_from_path(path) + + if found: + return len(found) + else: + return None + + +def get_frame_from_path(path): + """ + Return sequence number from Flame path style + + Args: + path (str): path url or simple file name + + Returns: + int: sequence frame number + + Example: + def get_frame_from_path(path): + ("plate.0001.exr") > 0001 + + """ + frame_pattern = re.compile(r"[._](\d+)[.]") + + found = re.findall(frame_pattern, path) + + if found: + return found.pop() + else: + return None diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py new file mode 100644 index 0000000000..9a80a92414 --- /dev/null +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -0,0 +1,26 @@ +import pyblish.api +import openpype.hosts.flame as opflame +from openpype.hosts.flame.otio import flame_export as otio_export +from openpype.hosts.flame.api import lib +from pprint import pformat +reload(lib) # noqa +reload(otio_export) # noqa + + +@pyblish.api.log +class CollectTestSelection(pyblish.api.ContextPlugin): + """testing selection sharing + """ + + order = pyblish.api.CollectorOrder + label = "test selection" + hosts = ["flame"] + + def process(self, context): + self.log.info(opflame.selection) + + sequence = lib.get_current_sequence(opflame.selection) + + otio_timeline = otio_export.create_otio_timeline(sequence) + + self.log.info(pformat(otio_timeline))