diff --git a/openpype/hosts/hiero/api/lib.py b/openpype/hosts/hiero/api/lib.py index a9982d96c4a..d8a235be775 100644 --- a/openpype/hosts/hiero/api/lib.py +++ b/openpype/hosts/hiero/api/lib.py @@ -214,7 +214,9 @@ def get_track_items( # add all if no track_type is defined return_list.append(track_item) - return return_list + # return output list but make sure all items are TrackItems + return [_i for _i in return_list + if type(_i) == hiero.core.TrackItem] def get_track_item_pype_tag(track_item): diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/extract_clip_effects.py b/openpype/hosts/hiero/plugins/publish/extract_clip_effects.py similarity index 92% rename from openpype/hosts/hiero/plugins/publish_old_workflow/extract_clip_effects.py rename to openpype/hosts/hiero/plugins/publish/extract_clip_effects.py index d2ac7f47864..5b0aa270a78 100644 --- a/openpype/hosts/hiero/plugins/publish_old_workflow/extract_clip_effects.py +++ b/openpype/hosts/hiero/plugins/publish/extract_clip_effects.py @@ -52,10 +52,11 @@ def process(self, instance): instance.data["representations"] = list() transfer_data = [ - "handleStart", "handleEnd", "sourceIn", "sourceOut", - "frameStart", "frameEnd", "sourceInH", "sourceOutH", - "clipIn", "clipOut", "clipInH", "clipOutH", "asset", "track", - "version" + "handleStart", "handleEnd", + "sourceStart", "sourceStartH", "sourceEnd", "sourceEndH", + "frameStart", "frameEnd", + "clipIn", "clipOut", "clipInH", "clipOutH", + "asset", "version" ] # pass data to version diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_clip_effects.py b/openpype/hosts/hiero/plugins/publish/precollect_clip_effects.py similarity index 93% rename from openpype/hosts/hiero/plugins/publish_old_workflow/precollect_clip_effects.py rename to openpype/hosts/hiero/plugins/publish/precollect_clip_effects.py index f9bde24255f..5a9f89651c7 100644 --- a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_clip_effects.py +++ b/openpype/hosts/hiero/plugins/publish/precollect_clip_effects.py @@ -5,7 +5,7 @@ class PreCollectClipEffects(pyblish.api.InstancePlugin): """Collect soft effects instances.""" - order = pyblish.api.CollectorOrder - 0.508 + order = pyblish.api.CollectorOrder - 0.579 label = "Pre-collect Clip Effects Instances" families = ["clip"] @@ -24,7 +24,8 @@ def process(self, instance): self.clip_in_h = self.clip_in - self.handle_start self.clip_out_h = self.clip_out + self.handle_end - track = instance.data["trackItem"] + track_item = instance.data["item"] + track = track_item.parent() track_index = track.trackIndex() tracks_effect_items = instance.context.data.get("tracksEffectItems") clip_effect_items = instance.data.get("clipEffectItems") @@ -112,7 +113,12 @@ def add_effect(self, track_index, sitem): node = sitem.node() node_serialized = self.node_serialisation(node) node_name = sitem.name() - node_class = re.sub(r"\d+", "", node_name) + + if "_" in node_name: + node_class = re.sub(r"(?:_)[_0-9]+", "", node_name) # more numbers + else: + node_class = re.sub(r"\d+", "", node_name) # one number + # collect timelineIn/Out effect_t_in = int(sitem.timelineIn()) effect_t_out = int(sitem.timelineOut()) @@ -121,6 +127,7 @@ def add_effect(self, track_index, sitem): return self.log.debug("node_name: `{}`".format(node_name)) + self.log.debug("node_class: `{}`".format(node_class)) return {node_name: { "class": node_class, diff --git a/openpype/hosts/hiero/plugins/publish/precollect_instances.py b/openpype/hosts/hiero/plugins/publish/precollect_instances.py index 8cccdec99a2..f7449561eff 100644 --- a/openpype/hosts/hiero/plugins/publish/precollect_instances.py +++ b/openpype/hosts/hiero/plugins/publish/precollect_instances.py @@ -2,6 +2,9 @@ import openpype from openpype.hosts.hiero import api as phiero from openpype.hosts.hiero.otio import hiero_export +import hiero + +from compiler.ast import flatten # # developer reload modules from pprint import pformat @@ -14,18 +17,40 @@ class PrecollectInstances(pyblish.api.ContextPlugin): label = "Precollect Instances" hosts = ["hiero"] + audio_track_items = [] + def process(self, context): - otio_timeline = context.data["otioTimeline"] + self.otio_timeline = context.data["otioTimeline"] + selected_timeline_items = phiero.get_track_items( - selected=True, check_enabled=True, check_tagged=True) + selected=True, check_tagged=True, check_enabled=True) + + # only return enabled track items + if not selected_timeline_items: + selected_timeline_items = phiero.get_track_items( + check_enabled=True, check_tagged=True) + self.log.info( "Processing enabled track items: {}".format( selected_timeline_items)) + # add all tracks subtreck effect items to context + all_tracks = hiero.ui.activeSequence().videoTracks() + tracks_effect_items = self.collect_sub_track_items(all_tracks) + context.data["tracksEffectItems"] = tracks_effect_items + + # process all sellected timeline track items for track_item in selected_timeline_items: data = {} clip_name = track_item.name() + source_clip = track_item.source() + + # get clips subtracks and anotations + annotations = self.clip_annotations(source_clip) + subtracks = self.clip_subtrack(track_item) + self.log.debug("Annotations: {}".format(annotations)) + self.log.debug(">> Subtracks: {}".format(subtracks)) # get openpype tag data tag_data = phiero.get_track_item_pype_data(track_item) @@ -76,12 +101,15 @@ def process(self, context): "item": track_item, "families": families, "publish": tag_data["publish"], - "fps": context.data["fps"] + "fps": context.data["fps"], + + # clip's effect + "clipEffectItems": subtracks, + "clipAnnotations": annotations }) # otio clip data - otio_data = self.get_otio_clip_instance_data( - otio_timeline, track_item) or {} + otio_data = self.get_otio_clip_instance_data(track_item) or {} self.log.debug("__ otio_data: {}".format(pformat(otio_data))) data.update(otio_data) self.log.debug("__ data: {}".format(pformat(data))) @@ -185,6 +213,10 @@ def create_audio_instance(self, context, **data): item = data.get("item") clip_name = item.name() + # test if any audio clips + if not self.test_any_audio(item): + return + asset = data["asset"] subset = "audioMain" @@ -215,7 +247,28 @@ def create_audio_instance(self, context, **data): self.log.debug( "_ instance.data: {}".format(pformat(instance.data))) - def get_otio_clip_instance_data(self, otio_timeline, track_item): + def test_any_audio(self, track_item): + # collect all audio tracks to class variable + if not self.audio_track_items: + for otio_clip in self.otio_timeline.each_clip(): + if otio_clip.parent().kind != "Audio": + continue + self.audio_track_items.append(otio_clip) + + # get track item timeline range + timeline_range = self.create_otio_time_range_from_timeline_item_data( + track_item) + + # loop trough audio track items and search for overlaping clip + for otio_audio in self.audio_track_items: + parent_range = otio_audio.range_in_parent() + + # if any overaling clip found then return True + if openpype.lib.is_overlapping_otio_ranges( + parent_range, timeline_range, strict=False): + return True + + def get_otio_clip_instance_data(self, track_item): """ Return otio objects for timeline, track and clip @@ -231,7 +284,7 @@ def get_otio_clip_instance_data(self, otio_timeline, track_item): ti_track_name = track_item.parent().name() timeline_range = self.create_otio_time_range_from_timeline_item_data( track_item) - for otio_clip in otio_timeline.each_clip(): + for otio_clip in self.otio_timeline.each_clip(): track_name = otio_clip.parent().name parent_range = otio_clip.range_in_parent() if ti_track_name not in track_name: @@ -258,3 +311,76 @@ def create_otio_time_range_from_timeline_item_data(track_item): return hiero_export.create_otio_time_range( frame_start, frame_duration, fps) + + @staticmethod + def collect_sub_track_items(tracks): + """ + Returns dictionary with track index as key and list of subtracks + """ + # collect all subtrack items + sub_track_items = {} + for track in tracks: + items = track.items() + + # skip if no clips on track > need track with effect only + if items: + continue + + # skip all disabled tracks + if not track.isEnabled(): + continue + + track_index = track.trackIndex() + _sub_track_items = flatten(track.subTrackItems()) + + # continue only if any subtrack items are collected + if len(_sub_track_items) < 1: + continue + + enabled_sti = [] + # loop all found subtrack items and check if they are enabled + for _sti in _sub_track_items: + # checking if not enabled + if not _sti.isEnabled(): + continue + if isinstance(_sti, hiero.core.Annotation): + continue + # collect the subtrack item + enabled_sti.append(_sti) + + # continue only if any subtrack items are collected + if len(enabled_sti) < 1: + continue + + # add collection of subtrackitems to dict + sub_track_items[track_index] = enabled_sti + + return sub_track_items + + @staticmethod + def clip_annotations(clip): + """ + Returns list of Clip's hiero.core.Annotation + """ + annotations = [] + subTrackItems = flatten(clip.subTrackItems()) + annotations += [item for item in subTrackItems if isinstance( + item, hiero.core.Annotation)] + return annotations + + @staticmethod + def clip_subtrack(clip): + """ + Returns list of Clip's hiero.core.SubTrackItem + """ + subtracks = [] + subTrackItems = flatten(clip.parent().subTrackItems()) + for item in subTrackItems: + # avoid all anotation + if isinstance(item, hiero.core.Annotation): + continue + # # avoid all not anaibled + if not item.isEnabled(): + continue + subtracks.append(item) + return subtracks diff --git a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py index bc4ef7e150b..530a433423a 100644 --- a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py +++ b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py @@ -75,10 +75,26 @@ def process(self, context): "activeProject": project, "otioTimeline": otio_timeline, "currentFile": curent_file, - "fps": fps, + "colorspace": self.get_colorspace(project), + "fps": fps } context.data.update(context_data) self.log.info("Creating instance: {}".format(instance)) self.log.debug("__ instance.data: {}".format(pformat(instance.data))) self.log.debug("__ context_data: {}".format(pformat(context_data))) + + def get_colorspace(self, project): + # get workfile's colorspace properties + return { + "useOCIOEnvironmentOverride": project.useOCIOEnvironmentOverride(), + "lutSetting16Bit": project.lutSetting16Bit(), + "lutSetting8Bit": project.lutSetting8Bit(), + "lutSettingFloat": project.lutSettingFloat(), + "lutSettingLog": project.lutSettingLog(), + "lutSettingViewer": project.lutSettingViewer(), + "lutSettingWorkingSpace": project.lutSettingWorkingSpace(), + "lutUseOCIOForExport": project.lutUseOCIOForExport(), + "ocioConfigName": project.ocioConfigName(), + "ocioConfigPath": project.ocioConfigPath() + } diff --git a/openpype/hosts/nuke/plugins/load/load_luts.py b/openpype/hosts/nuke/plugins/load/load_effects.py similarity index 94% rename from openpype/hosts/nuke/plugins/load/load_luts.py rename to openpype/hosts/nuke/plugins/load/load_effects.py index 85ec3e2060a..6306767f376 100644 --- a/openpype/hosts/nuke/plugins/load/load_luts.py +++ b/openpype/hosts/nuke/plugins/load/load_effects.py @@ -4,18 +4,19 @@ from collections import OrderedDict -class LoadLuts(api.Loader): +class LoadEffects(api.Loader): """Loading colorspace soft effect exported from nukestudio""" - representations = ["lutJson"] - families = ["lut"] + representations = ["effectJson"] + families = ["effect"] - label = "Load Luts - nodes" + label = "Load Effects - nodes" order = 0 icon = "cc" color = style.colors.light ignore_attr = ["useLifetime"] + def load(self, context, name, namespace, data): """ Loading function to get the soft effects to particular read node @@ -66,15 +67,15 @@ def load(self, context, name, namespace, data): for key, value in json.load(f).iteritems()} # get correct order of nodes by positions on track and subtrack - nodes_order = self.reorder_nodes(json_f["effects"]) + nodes_order = self.reorder_nodes(json_f) # adding nodes to node graph # just in case we are in group lets jump out of it nuke.endGroup() - GN = nuke.createNode("Group") - - GN["name"].setValue(object_name) + GN = nuke.createNode( + "Group", + "name {}_1".format(object_name)) # adding content to the group node with GN: @@ -186,7 +187,7 @@ def update(self, container, representation): for key, value in json.load(f).iteritems()} # get correct order of nodes by positions on track and subtrack - nodes_order = self.reorder_nodes(json_f["effects"]) + nodes_order = self.reorder_nodes(json_f) # adding nodes to node graph # just in case we are in group lets jump out of it @@ -266,7 +267,11 @@ def connect_read_node(self, group_node, asset, subset): None: if nothing found """ search_name = "{0}_{1}".format(asset, subset) - node = [n for n in nuke.allNodes() if search_name in n["name"].value()] + + node = [ + n for n in nuke.allNodes(filter="Read") + if search_name in n["file"].value() + ] if len(node) > 0: rn = node[0] else: @@ -286,8 +291,10 @@ def connect_read_node(self, group_node, asset, subset): def reorder_nodes(self, data): new_order = OrderedDict() - trackNums = [v["trackIndex"] for k, v in data.items()] - subTrackNums = [v["subTrackIndex"] for k, v in data.items()] + trackNums = [v["trackIndex"] for k, v in data.items() + if isinstance(v, dict)] + subTrackNums = [v["subTrackIndex"] for k, v in data.items() + if isinstance(v, dict)] for trackIndex in range( min(trackNums), max(trackNums) + 1): @@ -300,6 +307,7 @@ def reorder_nodes(self, data): def get_item(self, data, trackIndex, subTrackIndex): return {key: val for key, val in data.items() + if isinstance(val, dict) if subTrackIndex == val["subTrackIndex"] if trackIndex == val["trackIndex"]} diff --git a/openpype/hosts/nuke/plugins/load/load_luts_ip.py b/openpype/hosts/nuke/plugins/load/load_effects_ip.py similarity index 95% rename from openpype/hosts/nuke/plugins/load/load_luts_ip.py rename to openpype/hosts/nuke/plugins/load/load_effects_ip.py index a0af29c7f4d..6c71f2ae160 100644 --- a/openpype/hosts/nuke/plugins/load/load_luts_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_effects_ip.py @@ -5,13 +5,13 @@ from openpype.hosts.nuke.api import lib -class LoadLutsInputProcess(api.Loader): +class LoadEffectsInputProcess(api.Loader): """Loading colorspace soft effect exported from nukestudio""" - representations = ["lutJson"] - families = ["lut"] + representations = ["effectJson"] + families = ["effect"] - label = "Load Luts - Input Process" + label = "Load Effects - Input Process" order = 0 icon = "eye" color = style.colors.alert @@ -67,15 +67,15 @@ def load(self, context, name, namespace, data): for key, value in json.load(f).iteritems()} # get correct order of nodes by positions on track and subtrack - nodes_order = self.reorder_nodes(json_f["effects"]) + nodes_order = self.reorder_nodes(json_f) # adding nodes to node graph # just in case we are in group lets jump out of it nuke.endGroup() - GN = nuke.createNode("Group") - - GN["name"].setValue(object_name) + GN = nuke.createNode( + "Group", + "name {}_1".format(object_name)) # adding content to the group node with GN: @@ -190,7 +190,7 @@ def update(self, container, representation): for key, value in json.load(f).iteritems()} # get correct order of nodes by positions on track and subtrack - nodes_order = self.reorder_nodes(json_f["effects"]) + nodes_order = self.reorder_nodes(json_f) # adding nodes to node graph # just in case we are in group lets jump out of it @@ -304,8 +304,10 @@ def connect_active_viewer(self, group_node): def reorder_nodes(self, data): new_order = OrderedDict() - trackNums = [v["trackIndex"] for k, v in data.items()] - subTrackNums = [v["subTrackIndex"] for k, v in data.items()] + trackNums = [v["trackIndex"] for k, v in data.items() + if isinstance(v, dict)] + subTrackNums = [v["subTrackIndex"] for k, v in data.items() + if isinstance(v, dict)] for trackIndex in range( min(trackNums), max(trackNums) + 1): @@ -318,6 +320,7 @@ def reorder_nodes(self, data): def get_item(self, data, trackIndex, subTrackIndex): return {key: val for key, val in data.items() + if isinstance(val, dict) if subTrackIndex == val["subTrackIndex"] if trackIndex == val["trackIndex"]} diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index 04a33cd5be5..98b59332dac 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -39,7 +39,6 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): "rig", "plate", "look", - "lut", "yetiRig", "yeticache", "nukenodes", @@ -52,7 +51,8 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): "fbx", "textures", "action", - "background" + "background", + "effect" ] def process(self, instance): diff --git a/openpype/plugins/publish/extract_otio_audio_tracks.py b/openpype/plugins/publish/extract_otio_audio_tracks.py index 43e40097f75..7ba55d0c396 100644 --- a/openpype/plugins/publish/extract_otio_audio_tracks.py +++ b/openpype/plugins/publish/extract_otio_audio_tracks.py @@ -40,12 +40,15 @@ def process(self, context): # get sequence otio_timeline = context.data["otioTimeline"] - # temp file - audio_temp_fpath = self.create_temp_file("audio") - # get all audio inputs from otio timeline audio_inputs = self.get_audio_track_items(otio_timeline) + if not audio_inputs: + return + + # temp file + audio_temp_fpath = self.create_temp_file("audio") + # create empty audio with longest duration empty = self.create_empty(audio_inputs) diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 9769f0d165c..3a926789fb8 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -78,7 +78,6 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "rig", "plate", "look", - "lut", "audio", "yetiRig", "yeticache", @@ -97,7 +96,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "editorial", "background", "camerarig", - "redshiftproxy" + "redshiftproxy", + "effect" ] exclude_families = ["clip"] db_representation_context_keys = [