From 7ff279c290b50f964801a12e18c6d20dd6748446 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sun, 9 Jun 2019 17:43:28 +0200 Subject: [PATCH 01/26] feat(nukestudio): initialize collecting effects (sub track item) --- .../nukestudio/publish/collect_effects.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 pype/plugins/nukestudio/publish/collect_effects.py diff --git a/pype/plugins/nukestudio/publish/collect_effects.py b/pype/plugins/nukestudio/publish/collect_effects.py new file mode 100644 index 0000000000..7643f1810e --- /dev/null +++ b/pype/plugins/nukestudio/publish/collect_effects.py @@ -0,0 +1,51 @@ +import pyblish.api +import hiero.core + + +class CollectVideoTracksEffects(pyblish.api.ContextPlugin): + """Collect video tracks effects into context.""" + + order = pyblish.api.CollectorOrder + 0.1 + label = "Effects from video tracks" + + def process(self, context): + # taking active sequence + sequence = context.data['activeSequence'] + + # adding ignoring knob keys + _ignoring_keys = ['invert_mask', 'help', 'mask', + 'xpos', 'ypos', 'layer', 'process_mask', 'channel', + 'channels', 'maskChannelMask', 'maskChannelInput', + 'note_font', 'note_font_size', 'unpremult', + 'postage_stamp_frame', 'maskChannel', 'export_cc', + 'select_cccid', 'mix', 'version'] + + # creating context attribute + context.data["effectTrackItems"] = effects = dict() + + # loop trough all videotracks + for track_index, video_track in enumerate(sequence.videoTracks()): + # loop trough all available subtracks + for subtrack_item in video_track.subTrackItems(): + # ignore anything not EffectTrackItem + if isinstance(subtrack_item[0], hiero.core.EffectTrackItem): + et_item = subtrack_item[0] + # ignore item if not enabled + if et_item.isEnabled(): + node = et_item.node() + node_serialized = {} + # loop trough all knobs and collect not ignored + # and any with any value + for knob in node.knobs().keys(): + if (knob not in _ignoring_keys) and node[knob].value(): + node_serialized[knob] = node[knob].value() + # add it to the context attribute + effects.update({et_item.name(): { + "timelineIn": int(et_item.timelineIn()), + "timelineOut": int(et_item.timelineOut()), + "subTrackIndex": et_item.subTrackIndex(), + "trackIndex": track_index, + "node": node_serialized + }}) + + self.log.debug("effects: {}".format(effects)) From 7ced179b8ed3951ee586b1a9f85a721707519233 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 13 Aug 2019 08:37:14 +0200 Subject: [PATCH 02/26] fix(nk): cleaning up --- pype/nuke/lib.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 82244afdb5..c930f49b63 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -339,7 +339,7 @@ def create_write_node(name, data, prenodes=None): # adding write to read button add_button_write_to_read(GN) - + divider = nuke.Text_Knob('') GN.addKnob(divider) @@ -382,7 +382,7 @@ def set_viewers_colorspace(viewer): ''' Adds correct colorspace to viewer Arguments: - viewer (obj): nuke viewer node object to be fixed + viewer (dict): adjustments from presets ''' assert isinstance(viewer, dict), log.error( @@ -401,7 +401,7 @@ def set_viewers_colorspace(viewer): copy_inputs = v.dependencies() copy_knobs = {k: v[k].value() for k in v.knobs() if k not in filter_knobs} - pprint(copy_knobs) + # delete viewer with wrong settings erased_viewers.append(v['name'].value()) nuke.delete(v) @@ -431,7 +431,7 @@ def set_root_colorspace(root_dict): ''' Adds correct colorspace to root Arguments: - root_dict (dict): nuke root node as dictionary + root_dict (dict): adjustmensts from presets ''' assert isinstance(root_dict, dict), log.error( @@ -496,10 +496,12 @@ def set_colorspace(): "contact your supervisor!") -def reset_frame_range_handles(): +def reset_frame_range_handles(root=None): """Set frame range to current asset""" - root = nuke.root() + if not root: + root = nuke.root() + name = api.Session["AVALON_ASSET"] asset_entity = pype.get_asset(name) From de4c0192268124c08f3f83686d46a9fb720c0aa3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 13 Aug 2019 08:39:15 +0200 Subject: [PATCH 03/26] feat(nks): wip create nk method - will be building nk scripts from nks - will create clip on timeline with linked precomp - will create project clip in bin with correct hierarchy --- pype/nukestudio/lib.py | 112 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index 6674e8a3aa..c7e15d8d67 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -189,7 +189,7 @@ def add_submission(): class PublishAction(QtWidgets.QAction): """ - Action with is showing as menu item + Action with is showing as menu item """ def __init__(self): @@ -287,3 +287,113 @@ def _show_no_gui(): messagebox.setStandardButtons(messagebox.Ok) messagebox.exec_() + + +def create_nk(filepath, version, representations, nodes=None, to_timeline=False): + ''' Creating nuke workfile with particular version with given nodes + Also it is creating timeline track items as precomps. + + Arguments: + filepath(str): path to workfile to be created + version(obj): entity avalon db + representations(list dict): entities from avalon db + nodes(list of dict): each key in dict is knob order is important + to_timeline(type): will build trackItem with metadata + + Returns: + bool: True if done + + Raises: + Exception: with traceback + + ''' + import hiero.core + from avalon.nuke import imprint + from pype.nuke import ( + reset_frame_range_handles, + set_colorspace + ) + # check if the file exists if does then Raise "File exists!" + if os.path.exists(filepath): + raise FileExistsError("File already exists: `{}`".format(filepath)) + + # if no representations matching then + # Raise "no representations to be build" + if len(representations) == 0: + raise AttributeError("Missing list of `representations`") + + # check nodes input + if len(nodes) == 0: + log.warning("Missing list of `nodes`") + + # create temp nk file + nuke_script = hiero.core.nuke.ScriptWriter() + + version_data = version.get("data", {}) + + if not "frameStart" not in version_data.keys(): + raise AttributeError("Missing attribute of version: `frameStart`") + + # editorial + first_frame = version_data.get("frameStart") + last_frame = version_data.get("frameEnd") + fps = version_data.get("fps") + + # setting + colorspace = version_data.get("colorspaceScript") + widht = version_data.get("widht") + height = version_data.get("height") + pixel_aspect = version_data.get("pixelAspect") + + # handles + handle_start = version_data.get("handleStart") + handle_end = version_data.get("handleEnd") + + # create root node and save all metadata + root_node = hiero.core.nuke.RootNode( + first_frame, + last_frame, + fps=fps + ) + + # run set colorspace, set framerange, set format + # set colorspace from 'colorspaceScript' + # root_node.addProjectSettings(colorspace) + + # add root knob AvalonTab and data + publish knob + # imprint(root_node, { + # "handleStart": int(handle_start), + # "handleEnd": int(handle_end) + # }) + + nuke_script.addNode(root_node) + + write_node = hiero.core.nuke.WriteNode(movie_path.replace("\\", "/")) + write_node.setKnob("file_type", "mov") + write_node.setKnob("mov32_audiofile", audio_file.replace("\\", "/")) + write_node.setKnob("mov32_fps", sequence.framerate()) + nuke_script.addNode(write_node) + + nuke_script.writeToDisk(nukescript_path) + + + + + # create read nodes with Loader plugin from matched representations + + # create subsets in workfile + for repr in representations: + subset = repr.get("subset") + id = repr.get("id") + data = repr.get("data") + + # check if all variables are filled + if subset and id and data: + # check if names from `representations` are in db + # set mov files for correct colorspace + else: + raise KeyError("Missing key in `representation`") + + + + # Create and connect rendering write From 8635f6b532e0c01273b0400579e7be42c197f33b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 13 Aug 2019 08:40:10 +0200 Subject: [PATCH 04/26] fix(nks): adding width, height, pixelAspect into instance sharing attributes --- .../publish/collect_hierarchy_context.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index 536abf5ba4..5f29837d80 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -38,6 +38,10 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): tags = instance.data.get("tags", None) clip = instance.data["item"] asset = instance.data.get("asset") + sequence = context.data['activeSequence'] + width = int(sequence.format().width()) + height = int(sequence.format().height()) + pixel_aspect = sequence.format().pixelAspect() # build data for inner nukestudio project property data = { @@ -157,6 +161,9 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): "asset": asset, "hierarchy": hierarchy, "parents": parents, + "width": width, + "height": height, + "pixelAspect": pixel_aspect, "tasks": instance.data["tasks"] }) @@ -191,7 +198,7 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin): def process(self, context): instances = context[:] - sequence = context.data['activeSequence'] + # create hierarchyContext attr if context has none temp_context = {} @@ -216,6 +223,9 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin): instance.data["parents"] = s_asset_data["parents"] instance.data["hierarchy"] = s_asset_data["hierarchy"] instance.data["tasks"] = s_asset_data["tasks"] + instance.data["width"] = s_asset_data["width"] + instance.data["height"] = s_asset_data["height"] + instance.data["pixelAspect"] = s_asset_data["pixelAspect"] # adding frame start if any on instance start_frame = s_asset_data.get("startingFrame") @@ -265,16 +275,10 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin): # adding SourceResolution if Tag was present if instance.data.get("main"): - width = int(sequence.format().width()) - height = int(sequence.format().height()) - pixel_aspect = sequence.format().pixelAspect() - self.log.info("Sequence Width,Height,PixelAspect are: `{0},{1},{2}`".format( - width, height, pixel_aspect)) - in_info['custom_attributes'].update({ - "resolutionWidth": width, - "resolutionHeight": height, - "pixelAspect": pixel_aspect + "resolutionWidth": instance.data["width"], + "resolutionHeight": instance.data["height"], + "pixelAspect": instance.data["pixelAspect"] }) in_info['tasks'] = instance.data['tasks'] From d4dc2e96b750a48d3bcbf8c54ab9cc0566dc82e0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 13 Aug 2019 08:40:43 +0200 Subject: [PATCH 05/26] feat(nks): adding width, height, pixelAspect into version data --- pype/plugins/nukestudio/publish/collect_plates.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_plates.py b/pype/plugins/nukestudio/publish/collect_plates.py index 9843307f14..7f6f4138cb 100644 --- a/pype/plugins/nukestudio/publish/collect_plates.py +++ b/pype/plugins/nukestudio/publish/collect_plates.py @@ -66,11 +66,14 @@ class CollectPlates(api.InstancePlugin): item = instance.data["item"] width = int(item.source().mediaSource().width()) height = int(item.source().mediaSource().height()) - self.log.info("Source Width and Height are: `{0} x {1}`".format( - width, height)) + pixel_aspect = int(item.source().mediaSource().pixelAspect()) + + self.log.info("Source Width and Height are: `{0} x {1} : {2}`".format( + width, height, pixel_aspect)) data.update({ "width": width, - "height": height + "height": height, + "pixelAspect": pixel_aspect }) self.log.debug("Creating instance with name: {}".format(data["name"])) @@ -123,7 +126,7 @@ class CollectPlatesData(api.InstancePlugin): transfer_data = [ "handleStart", "handleEnd", "sourceIn", "sourceOut", "frameStart", "frameEnd", "sourceInH", "sourceOutH", "clipIn", "clipOut", - "clipInH", "clipOutH", "asset", "track", "version" + "clipInH", "clipOutH", "asset", "track", "version", "width", "height", "pixelAspect" ] # pass data to version @@ -133,6 +136,7 @@ class CollectPlatesData(api.InstancePlugin): version_data.update({ "handles": version_data['handleStart'], "colorspace": item.sourceMediaColourTransform(), + "colorspaceScript": instance.context.data["colorspace"], "families": [f for f in families if 'ftrack' not in f], "subset": name, "fps": instance.context.data["fps"] From 814e7cd204efe151f784b3160cc0b6e9acadc818 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 13 Aug 2019 16:15:41 +0200 Subject: [PATCH 06/26] feat(global): adding `get_subsets()` for quering all last versions of representations for subsets of given asset --- pype/lib.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/pype/lib.py b/pype/lib.py index 7009743c97..18adfa355d 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -483,3 +483,72 @@ def filter_pyblish_plugins(plugins): option, value, plugin.__name__)) setattr(plugin, option, value) + + +def get_subsets(asset_name, + regex_filter=None, + version="last", + representations=["exr", "dpx"]): + """ + Query subsets with filter on name. + + The method will return all found subsets and its defined version and subsets. Version could be specified with number. Representation can be filtered. + + Arguments: + asset_name (str): asset (shot) name + regex_filter (raw): raw string with filter pattern + version (str or int): `last` or number of version + representations (list): list for all representations + + Returns: + dict: subsets with version and representaions in keys + """ + from avalon import io + + # query asset from db + asset_io = io.find_one({"type": "asset", + "name": asset_name}) + + # check if anything returned + assert asset_io, "Asset not existing. \ + Check correct name: `{}`".format(asset_name) + + # create subsets query filter + filter_query = {"type": "subset", "parent": asset_io["_id"]} + + # add reggex filter string into query filter + if regex_filter: + filter_query.update({"name": {"$regex": r"{}".format(regex_filter)}}) + else: + filter_query.update({"name": {"$regex": r'.*'}}) + + # query all assets + subsets = [s for s in io.find(filter_query)] + + assert subsets, "No subsets found. Check correct filter. Try this for start `r'.*'`: asset: `{}`".format(asset_name) + + output_dict = {} + # Process subsets + for subset in subsets: + if "last" in str(version): + version_sel = io.find_one({"type": "version", + "parent": subset["_id"]}, + sort=[("name", -1)]) + else: + version_sel = io.find_one({"type": "version", + "parent": subset["_id"], + "name": int(version)}) + + find_dict = {"type": "representation", + "parent": version_sel["_id"]} + + filter_repr = {"$or": [{"name": repr} for repr in representations]} + + find_dict.update(filter_repr) + repres_out = [i for i in io.find(find_dict)] + + if len(repres_out) > 0: + output_dict[subset["name"]] = {"version": version_sel, + "representaions": repres_out} + + return output_dict From 67acc5a4c99a8d67e81d0a8b01e397522941882d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 13 Aug 2019 18:08:13 +0200 Subject: [PATCH 07/26] feat(nk): converting `workfile setting` methods into class and its methods --- pype/nuke/lib.py | 534 +++++++++++++++++++++++++---------------------- 1 file changed, 286 insertions(+), 248 deletions(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 4dfd326066..2463e4474b 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -339,7 +339,7 @@ def create_write_node(name, data, prenodes=None): # adding write to read button add_button_write_to_read(GN) - + divider = nuke.Text_Knob('') GN.addKnob(divider) @@ -378,130 +378,117 @@ def add_rendering_knobs(node): return node -def set_viewers_colorspace(viewer): - ''' Adds correct colorspace to viewer +class Workfile_settings(object): + """ + All settings for workfile will be set + + This object is setting all possible root settings to the workfile. + Including Colorspace, Frame ranges, Resolution format. It can set it + to Root node or to any given node. Arguments: - viewer (obj): nuke viewer node object to be fixed + root (node): nuke's root node + nodes (list): list of nuke's nodes + nodes_filter (list): filtering classes for nodes - ''' - assert isinstance(viewer, dict), log.error( - "set_viewers_colorspace(): argument should be dictionary") + """ + def __init__(self, root=None, nodes=None, nodes_filter=[]): + self._project = io.find_one({"type": "project"}) + self._asset = api.Session["AVALON_ASSET"] + self._asset_entity = pype.get_asset(self._asset) + self._root = root or nuke.root() + self._nodes = nodes or [] + self._nodes_filter = nodes_filter - filter_knobs = [ - "viewerProcess", - "wipe_position" - ] - viewers = [n for n in nuke.allNodes() if n.Class() == 'Viewer'] - erased_viewers = [] + def get_nodes(self): + if (self._nodes is []) and (self._nodes_filter is not []): + for filter in self._nodes_filter: + self._nodes += nuke.allNodes(filter=filter) + elif (self._nodes is not []) and (self._nodes_filter is not []): + for filter in self._nodes_filter: + self._nodes += [n for n in self._nodes if filter in n.Class()] + elif (self._nodes is []) and (self._nodes_filter is []): + self._nodes += [n for n in nuke.allNodes()] - for v in viewers: - v['viewerProcess'].setValue(str(viewer["viewerProcess"])) - if str(viewer["viewerProcess"]) not in v['viewerProcess'].value(): - copy_inputs = v.dependencies() - copy_knobs = {k: v[k].value() for k in v.knobs() - if k not in filter_knobs} - pprint(copy_knobs) - # delete viewer with wrong settings - erased_viewers.append(v['name'].value()) - nuke.delete(v) + def set_viewers_colorspace(self, viewer_dict): + ''' Adds correct colorspace to viewer - # create new viewer - nv = nuke.createNode("Viewer") + Arguments: + viewer_dict (dict): adjustments from presets - # connect to original inputs - for i, n in enumerate(copy_inputs): - nv.setInput(i, n) + ''' + assert isinstance(viewer_dict, dict), log.error( + "set_viewers_colorspace(): argument should be dictionary") - # set coppied knobs - for k, v in copy_knobs.items(): - print(k, v) - nv[k].setValue(v) + filter_knobs = [ + "viewerProcess", + "wipe_position" + ] - # set viewerProcess - nv['viewerProcess'].setValue(str(viewer["viewerProcess"])) + erased_viewers = [] + for v in [n for n in self._nodes if "Viewer" in n.Class()]: + v['viewerProcess'].setValue(str(viewer_dict["viewerProcess"])) + if str(viewer_dict["viewerProcess"]) \ + not in v['viewerProcess'].value(): + copy_inputs = v.dependencies() + copy_knobs = {k: v[k].value() for k in v.knobs() + if k not in filter_knobs} - if erased_viewers: - log.warning( - "Attention! Viewer nodes {} were erased." - "It had wrong color profile".format(erased_viewers)) + # delete viewer with wrong settings + erased_viewers.append(v['name'].value()) + nuke.delete(v) + # create new viewer + nv = nuke.createNode("Viewer") -def set_root_colorspace(root_dict): - ''' Adds correct colorspace to root + # connect to original inputs + for i, n in enumerate(copy_inputs): + nv.setInput(i, n) - Arguments: - root_dict (dict): nuke root node as dictionary + # set coppied knobs + for k, v in copy_knobs.items(): + print(k, v) + nv[k].setValue(v) - ''' - assert isinstance(root_dict, dict), log.error( - "set_root_colorspace(): argument should be dictionary") + # set viewerProcess + nv['viewerProcess'].setValue(str(viewer_dict["viewerProcess"])) - # first set OCIO - if nuke.root()["colorManagement"].value() not in str(root_dict["colorManagement"]): - nuke.root()["colorManagement"].setValue( - str(root_dict["colorManagement"])) + if erased_viewers: + log.warning( + "Attention! Viewer nodes {} were erased." + "It had wrong color profile".format(erased_viewers)) - # second set ocio version - if nuke.root()["OCIO_config"].value() not in str(root_dict["OCIO_config"]): - nuke.root()["OCIO_config"].setValue(str(root_dict["OCIO_config"])) + def set_root_colorspace(self, root_dict): + ''' Adds correct colorspace to root - # then set the rest - for knob, value in root_dict.items(): - if nuke.root()[knob].value() not in value: - nuke.root()[knob].setValue(str(value)) - log.debug("nuke.root()['{}'] changed to: {}".format(knob, value)) + Arguments: + root_dict (dict): adjustmensts from presets + ''' + assert isinstance(root_dict, dict), log.error( + "set_root_colorspace(): argument should be dictionary") -def set_writes_colorspace(write_dict): - ''' Adds correct colorspace to write node dict + # first set OCIO + if self._root["colorManagement"].value() \ + not in str(root_dict["colorManagement"]): + self._root["colorManagement"].setValue( + str(root_dict["colorManagement"])) - Arguments: - write_dict (dict): nuke write node as dictionary + # second set ocio version + if self._root["OCIO_config"].value() \ + not in str(root_dict["OCIO_config"]): + self._root["OCIO_config"].setValue( + str(root_dict["OCIO_config"])) - ''' - # TODO: complete this function so any write node in scene will have fixed colorspace following presets for the project - assert isinstance(write_dict, dict), log.error( - "set_root_colorspace(): argument should be dictionary") + # then set the rest + for knob, value in root_dict.items(): + if self._root[knob].value() not in value: + self._root[knob].setValue(str(value)) + log.debug("nuke.root()['{}'] changed to: {}".format( + knob, value)) - log.debug("__ set_writes_colorspace(): {}".format(write_dict)) - - -def set_colorspace(): - ''' Setting colorpace following presets - ''' - nuke_colorspace = get_colorspace_preset().get("nuke", None) - - try: - set_root_colorspace(nuke_colorspace["root"]) - except AttributeError: - log.error( - "set_colorspace(): missing `root` settings in template") - try: - set_viewers_colorspace(nuke_colorspace["viewer"]) - except AttributeError: - log.error( - "set_colorspace(): missing `viewer` settings in template") - try: - set_writes_colorspace(nuke_colorspace["write"]) - except AttributeError: - log.error( - "set_colorspace(): missing `write` settings in template") - - try: - for key in nuke_colorspace: - log.debug("Preset's colorspace key: {}".format(key)) - except TypeError: - log.error("Nuke is not in templates! \n\n\n" - "contact your supervisor!") - - -def reset_frame_range_handles(): - """Set frame range to current asset""" - - root = nuke.root() - name = api.Session["AVALON_ASSET"] - asset_entity = pype.get_asset(name) + def set_writes_colorspace(self, write_dict): + ''' Adds correct colorspace to write node dict if "data" not in asset_entity: msg = "Asset {} don't have set any 'data'".format(name) @@ -509,170 +496,221 @@ def reset_frame_range_handles(): nuke.message(msg) return data = asset_entity["data"] + Arguments: + write_dict (dict): nuke write node as dictionary missing_cols = [] check_cols = ["fps", "frameStart", "frameEnd", "handleStart", "handleEnd"] + ''' + # TODO: complete this function so any write node in + # scene will have fixed colorspace following presets for the project + assert isinstance(write_dict, dict), log.error( + "set_root_colorspace(): argument should be dictionary") - for col in check_cols: - if col not in data: - missing_cols.append(col) + log.debug("__ set_writes_colorspace(): {}".format(write_dict)) - if len(missing_cols) > 0: - missing = ", ".join(missing_cols) - msg = "'{}' are not set for asset '{}'!".format(missing, name) - log.warning(msg) - nuke.message(msg) - return + def set_colorspace(self): + ''' Setting colorpace following presets + ''' + nuke_colorspace = get_colorspace_preset().get("nuke", None) - # get handles values - handle_start = asset_entity["data"]["handleStart"] - handle_end = asset_entity["data"]["handleEnd"] - - fps = asset_entity["data"]["fps"] - frame_start = int(asset_entity["data"]["frameStart"]) - handle_start - frame_end = int(asset_entity["data"]["frameEnd"]) + handle_end - - root["fps"].setValue(fps) - root["first_frame"].setValue(frame_start) - root["last_frame"].setValue(frame_end) - - # setting active viewers - nuke.frame(int(asset_entity["data"]["frameStart"])) - - range = '{0}-{1}'.format( - int(asset_entity["data"]["frameStart"]), - int(asset_entity["data"]["frameEnd"])) - - for node in nuke.allNodes(filter="Viewer"): - node['frame_range'].setValue(range) - node['frame_range_lock'].setValue(True) - node['frame_range'].setValue(range) - node['frame_range_lock'].setValue(True) - - # adding handle_start/end to root avalon knob - if not avalon.nuke.set_avalon_knob_data(root, { - "handleStart": int(handle_start), - "handleEnd": int(handle_end) - }): - log.warning("Cannot set Avalon knob to Root node!") - - -def reset_resolution(): - """Set resolution to project resolution.""" - log.info("Reseting resolution") - project = io.find_one({"type": "project"}) - asset = api.Session["AVALON_ASSET"] - asset = io.find_one({"name": asset, "type": "asset"}) - - width = asset.get('data', {}).get("resolutionWidth") - height = asset.get('data', {}).get("resolutionHeight") - pixel_aspect = asset.get('data', {}).get("pixelAspect") - - if any(not x for x in [width, height, pixel_aspect]): - log.error("Missing set shot attributes in DB. \nContact your supervisor!. \n\nWidth: `{0}` \nHeight: `{1}` \nPixel Asspect: `{2}`".format( - width, height, pixel_aspect)) - return - - bbox = asset.get('data', {}).get('crop') - - if bbox: try: - x, y, r, t = bbox.split(".") + self.set_root_colorspace(nuke_colorspace["root"]) + except AttributeError: + log.error( + "set_colorspace(): missing `root` settings in template") + try: + self.set_viewers_colorspace(nuke_colorspace["viewer"]) + except AttributeError: + log.error( + "set_colorspace(): missing `viewer` settings in template") + try: + self.set_writes_colorspace(nuke_colorspace["write"]) + except AttributeError: + log.error( + "set_colorspace(): missing `write` settings in template") + + try: + for key in nuke_colorspace: + log.debug("Preset's colorspace key: {}".format(key)) + except TypeError: + log.error("Nuke is not in templates! \n\n\n" + "contact your supervisor!") + + def reset_frame_range_handles(self): + """Set frame range to current asset""" + + if "data" not in self._asset_entity: + msg = "Asset {} don't have set any 'data'".format(self._asset) + log.warning(msg) + nuke.message(msg) + return + data = self._asset_entity["data"] + + missing_cols = [] + check_cols = ["fps", "frameStart", "frameEnd", + "handleStart", "handleEnd"] + + for col in check_cols: + if col not in data: + missing_cols.append(col) + + if len(missing_cols) > 0: + missing = ", ".join(missing_cols) + msg = "'{}' are not set for asset '{}'!".format( + missing, self._asset) + log.warning(msg) + nuke.message(msg) + return + + # get handles values + handle_start = data["handleStart"] + handle_end = data["handleEnd"] + + fps = data["fps"] + frame_start = int(data["frameStart"]) - handle_start + frame_end = int(data["frameEnd"]) + handle_end + + self._root["fps"].setValue(fps) + self._root["first_frame"].setValue(frame_start) + self._root["last_frame"].setValue(frame_end) + + # setting active viewers + try: + nuke.frame(int(data["frameStart"])) except Exception as e: - bbox = None - log.error("{}: {} \nFormat:Crop need to be set with dots, example: " - "0.0.1920.1080, /nSetting to default".format(__name__, e)) + log.warning("no viewer in scene: `{}`".format(e)) - used_formats = list() - for f in nuke.formats(): - if project["name"] in str(f.name()): - used_formats.append(f) - else: - format_name = project["name"] + "_1" + range = '{0}-{1}'.format( + int(data["frameStart"]), + int(data["frameEnd"])) - crnt_fmt_str = "" - if used_formats: - check_format = used_formats[-1] - format_name = "{}_{}".format( - project["name"], - int(used_formats[-1].name()[-1]) + 1 - ) - log.info( - "Format exists: {}. " - "Will create new: {}...".format( - used_formats[-1].name(), - format_name) - ) - crnt_fmt_kargs = { - "width": (check_format.width()), - "height": (check_format.height()), - "pixelAspect": float(check_format.pixelAspect()) + for node in nuke.allNodes(filter="Viewer"): + node['frame_range'].setValue(range) + node['frame_range_lock'].setValue(True) + node['frame_range'].setValue(range) + node['frame_range_lock'].setValue(True) + + # adding handle_start/end to root avalon knob + if not avalon.nuke.imprint(self._root, { + "handleStart": int(handle_start), + "handleEnd": int(handle_end) + }): + log.warning("Cannot set Avalon knob to Root node!") + + def reset_resolution(self): + """Set resolution to project resolution.""" + log.info("Reseting resolution") + + width = self._asset_entity.get('data', {}).get("resolutionWidth") + height = self._asset_entity.get('data', {}).get("resolutionHeight") + pixel_aspect = self._asset_entity.get('data', {}).get("pixelAspect") + + if any(not x for x in [width, height, pixel_aspect]): + log.error("Missing set shot attributes in DB. \nContact" + "your supervisor!. \n\nWidth: `{0}` \nHeight: `{1}`" + "\nPixel Asspect: `{2}`".format( + width, height, pixel_aspect)) + return + + bbox = self._asset_entity.get('data', {}).get('crop') + + if bbox: + try: + x, y, r, t = bbox.split(".") + except Exception as e: + bbox = None + log.error("{}: {} \nFormat:Crop need to be set with dots," + " example: 0.0.1920.1080, /nSetting to" + " default".format(__name__, e)) + + used_formats = list() + for f in nuke.formats(): + if self._project["name"] in str(f.name()): + used_formats.append(f) + else: + format_name = self._project["name"] + "_1" + + crnt_fmt_str = "" + if used_formats: + check_format = used_formats[-1] + format_name = "{}_{}".format( + self._project["name"], + int(used_formats[-1].name()[-1]) + 1 + ) + log.info( + "Format exists: {}. " + "Will create new: {}...".format( + used_formats[-1].name(), + format_name) + ) + crnt_fmt_kargs = { + "width": (check_format.width()), + "height": (check_format.height()), + "pixelAspect": float(check_format.pixelAspect()) + } + if bbox: + crnt_fmt_kargs.update({ + "x": int(check_format.x()), + "y": int(check_format.y()), + "r": int(check_format.r()), + "t": int(check_format.t()), + }) + crnt_fmt_str = self.make_format_string(**crnt_fmt_kargs) + + new_fmt_kargs = { + "width": int(width), + "height": int(height), + "pixelAspect": float(pixel_aspect), + "project_name": format_name } if bbox: - crnt_fmt_kargs.update({ - "x": int(check_format.x()), - "y": int(check_format.y()), - "r": int(check_format.r()), - "t": int(check_format.t()), + new_fmt_kargs.update({ + "x": int(x), + "y": int(y), + "r": int(r), + "t": int(t), }) - crnt_fmt_str = make_format_string(**crnt_fmt_kargs) - new_fmt_kargs = { - "width": int(width), - "height": int(height), - "pixelAspect": float(pixel_aspect), - "project_name": format_name - } - if bbox: - new_fmt_kargs.update({ - "x": int(x), - "y": int(y), - "r": int(r), - "t": int(t), - }) + new_fmt_str = self.make_format_string(**new_fmt_kargs) - new_fmt_str = make_format_string(**new_fmt_kargs) + if new_fmt_str not in crnt_fmt_str: + self.make_format(frm_str=new_fmt_str, + project_name=new_fmt_kargs["project_name"]) - if new_fmt_str not in crnt_fmt_str: - make_format(frm_str=new_fmt_str, - project_name=new_fmt_kargs["project_name"]) + log.info("Format is set") - log.info("Format is set") + def make_format_string(self, **args): + if args.get("r"): + return ( + "{width} " + "{height} " + "{x} " + "{y} " + "{r} " + "{t} " + "{pixelAspect:.2f}".format(**args) + ) + else: + return ( + "{width} " + "{height} " + "{pixelAspect:.2f}".format(**args) + ) + def make_format(self, **args): + log.info("Format does't exist, will create: \n{}".format(args)) + nuke.addFormat("{frm_str} " + "{project_name}".format(**args)) + self._root["format"].setValue("{project_name}".format(**args)) -def make_format_string(**args): - if args.get("r"): - return ( - "{width} " - "{height} " - "{x} " - "{y} " - "{r} " - "{t} " - "{pixelAspect:.2f}".format(**args) - ) - else: - return ( - "{width} " - "{height} " - "{pixelAspect:.2f}".format(**args) - ) - - -def make_format(**args): - log.info("Format does't exist, will create: \n{}".format(args)) - nuke.addFormat("{frm_str} " - "{project_name}".format(**args)) - nuke.root()["format"].setValue("{project_name}".format(**args)) - - -def set_context_settings(): - # replace reset resolution from avalon core to pype's - reset_resolution() - # replace reset resolution from avalon core to pype's - reset_frame_range_handles() - # add colorspace menu item - set_colorspace() + def set_context_settings(self): + # replace reset resolution from avalon core to pype's + self.reset_resolution() + # replace reset resolution from avalon core to pype's + self.reset_frame_range_handles() + # add colorspace menu item + self.set_colorspace() def get_hierarchical_attr(entity, attr, default=None): From 43c9a03e62fbc116f596372fbd34db79cfc3127c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 13 Aug 2019 18:08:50 +0200 Subject: [PATCH 08/26] feat(nuke): connecting correctly new `workfile settings` --- pype/nuke/__init__.py | 4 ++-- pype/nuke/menu.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pype/nuke/__init__.py b/pype/nuke/__init__.py index 0c4cdc10ab..997a423d79 100644 --- a/pype/nuke/__init__.py +++ b/pype/nuke/__init__.py @@ -104,7 +104,7 @@ def install(): avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled) - + workfile_settings = lib.Workfile_settings() # Disable all families except for the ones we explicitly want to see family_states = [ "write", @@ -121,7 +121,7 @@ def install(): nuke.addOnCreate(launch_workfiles_app, nodeClass="Root") # Set context settings. - nuke.addOnCreate(lib.set_context_settings, nodeClass="Root") + nuke.addOnCreate(workfile_settings.set_context_settings, nodeClass="Root") menu.install() diff --git a/pype/nuke/menu.py b/pype/nuke/menu.py index 4f5410f8fd..4d57f322e6 100644 --- a/pype/nuke/menu.py +++ b/pype/nuke/menu.py @@ -9,7 +9,7 @@ log = Logger().get_logger(__name__, "nuke") def install(): menubar = nuke.menu("Nuke") menu = menubar.findItem(Session["AVALON_LABEL"]) - + workfile_settings = lib.Workfile_settings() # replace reset resolution from avalon core to pype's name = "Reset Resolution" new_name = "Set Resolution" @@ -20,7 +20,7 @@ def install(): log.debug("Changing Item: {}".format(rm_item)) # rm_item[1].setEnabled(False) menu.removeItem(rm_item[1].name()) - menu.addCommand(new_name, lib.reset_resolution, index=(rm_item[0])) + menu.addCommand(new_name, workfile_settings.reset_resolution, index=(rm_item[0])) # replace reset frame range from avalon core to pype's name = "Reset Frame Range" @@ -31,12 +31,12 @@ def install(): log.debug("Changing Item: {}".format(rm_item)) # rm_item[1].setEnabled(False) menu.removeItem(rm_item[1].name()) - menu.addCommand(new_name, lib.reset_frame_range_handles, index=(rm_item[0])) + menu.addCommand(new_name, workfile_settings.reset_frame_range_handles, index=(rm_item[0])) # add colorspace menu item name = "Set colorspace" menu.addCommand( - name, lib.set_colorspace, + name, workfile_settings.set_colorspace, index=(rm_item[0]+2) ) log.debug("Adding menu item: {}".format(name)) @@ -44,7 +44,7 @@ def install(): # add item that applies all setting above name = "Apply all settings" menu.addCommand( - name, lib.set_context_settings, index=(rm_item[0]+3) + name, workfile_settings.set_context_settings, index=(rm_item[0]+3) ) log.debug("Adding menu item: {}".format(name)) From 6827cb82b4d56dd077565c90e6738989bd7a45af Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 14 Aug 2019 10:35:39 +0200 Subject: [PATCH 09/26] feat(nk): building first workfile wip --- pype/nuke/lib.py | 77 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 2463e4474b..0f30e8f76b 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -1,10 +1,12 @@ import os import sys +import getpass from collections import OrderedDict from pprint import pprint from avalon import api, io, lib import avalon.nuke import pype.api as pype + import nuke from .templates import ( get_colorspace_preset, @@ -12,6 +14,11 @@ from .templates import ( get_node_colorspace_preset ) +from .templates import ( + get_anatomy +) +# TODO: remove get_anatomy and import directly Anatomy() here + from pypeapp import Logger log = Logger().get_logger(__name__, "nuke") @@ -159,11 +166,6 @@ def format_anatomy(data): ''' # TODO: perhaps should be nonPublic - from .templates import ( - get_anatomy - ) - # TODO: remove get_anatomy and import directly Anatomy() here - anatomy = get_anatomy() log.debug("__ anatomy.templates: {}".format(anatomy.templates)) @@ -392,11 +394,11 @@ class Workfile_settings(object): nodes_filter (list): filtering classes for nodes """ - def __init__(self, root=None, nodes=None, nodes_filter=[]): + def __init__(self, asset=None, root_node=None, nodes=None, nodes_filter=[]): self._project = io.find_one({"type": "project"}) - self._asset = api.Session["AVALON_ASSET"] + self._asset = asset or api.Session["AVALON_ASSET"] self._asset_entity = pype.get_asset(self._asset) - self._root = root or nuke.root() + self._root_node = root_node or nuke.root() self._nodes = nodes or [] self._nodes_filter = nodes_filter @@ -469,21 +471,21 @@ class Workfile_settings(object): "set_root_colorspace(): argument should be dictionary") # first set OCIO - if self._root["colorManagement"].value() \ + if self._root_node["colorManagement"].value() \ not in str(root_dict["colorManagement"]): - self._root["colorManagement"].setValue( + self._root_node["colorManagement"].setValue( str(root_dict["colorManagement"])) # second set ocio version - if self._root["OCIO_config"].value() \ + if self._root_node["OCIO_config"].value() \ not in str(root_dict["OCIO_config"]): - self._root["OCIO_config"].setValue( + self._root_node["OCIO_config"].setValue( str(root_dict["OCIO_config"])) # then set the rest for knob, value in root_dict.items(): - if self._root[knob].value() not in value: - self._root[knob].setValue(str(value)) + if self._root_node[knob].value() not in value: + self._root_node[knob].setValue(str(value)) log.debug("nuke.root()['{}'] changed to: {}".format( knob, value)) @@ -571,9 +573,9 @@ class Workfile_settings(object): frame_start = int(data["frameStart"]) - handle_start frame_end = int(data["frameEnd"]) + handle_end - self._root["fps"].setValue(fps) - self._root["first_frame"].setValue(frame_start) - self._root["last_frame"].setValue(frame_end) + self._root_node["fps"].setValue(fps) + self._root_node["first_frame"].setValue(frame_start) + self._root_node["last_frame"].setValue(frame_end) # setting active viewers try: @@ -592,7 +594,7 @@ class Workfile_settings(object): node['frame_range_lock'].setValue(True) # adding handle_start/end to root avalon knob - if not avalon.nuke.imprint(self._root, { + if not avalon.nuke.imprint(self._root_node, { "handleStart": int(handle_start), "handleEnd": int(handle_end) }): @@ -702,7 +704,7 @@ class Workfile_settings(object): log.info("Format does't exist, will create: \n{}".format(args)) nuke.addFormat("{frm_str} " "{project_name}".format(**args)) - self._root["format"].setValue("{project_name}".format(**args)) + self._root_node["format"].setValue("{project_name}".format(**args)) def set_context_settings(self): # replace reset resolution from avalon core to pype's @@ -768,3 +770,40 @@ def get_write_node_template_attr(node): # fix badly encoded data return avalon.nuke.lib.fix_data_for_node_create(correct_data) + + +class Build_Workfile(Workfile_settings): + def __init__(self, root=None, asset_name=None, task=None, hierarchy=None, version=1): + Workfile_settings.__init__(self, asset=asset_name) + + ### create workfile path + # get project from database + project = self._project or io.find_one({"type": "project"}) + + # collect data for formating + data = { + "root": root or api.Session["AVALON_PROJECTS"], + "project": {"name": project["name"], + "code": project["data"].get("code", '')}, + "asset": asset_name or os.environ["AVALON_ASSET"], + "task": task or api.Session["AVALON_TASK"].lower(), + "hierarchy": hierarchy or pype.get_hierarchy(), + "version": version, + "user": getpass.getuser(), + "comment": "firstBuild" + } + + # get presets from anatomy + anatomy = get_anatomy() + # format anatomy + anatomy_filled = anatomy.format(data) + + # get dir and file for workfile + templ_work_dir = anatomy_filled["avalon"]["work"] + templ_work_file = anatomy_filled["avalon"]["workfile"] + ".nk" + + # save script as + path = os.path.join(templ_work_dir, templ_work_file).replace("\\", "/") + + + print(path) From 7a8a94b924def680be448120a49d4dae230d5491 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 14 Aug 2019 17:06:51 +0200 Subject: [PATCH 10/26] feat(pype): adding get subsets to api --- pype/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/api.py b/pype/api.py index 2c626f36a3..2c227b5b4b 100644 --- a/pype/api.py +++ b/pype/api.py @@ -23,6 +23,7 @@ from .lib import ( get_asset, get_project, get_hierarchy, + get_subsets, get_version_from_path, modified_environ, add_tool_to_environment @@ -53,6 +54,7 @@ __all__ = [ "get_project", "get_hierarchy", "get_asset", + "get_subsets", "get_version_from_path", "modified_environ", "add_tool_to_environment", From 64e1f45f34f13c856046c0fa1136976e09916c1e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 15 Aug 2019 10:41:28 +0200 Subject: [PATCH 11/26] feat(nk): implementing building workfile - also adding `get_subsets` to `pype.api` - adding `WorkfileSettings` class method to `pype.nuke.menu` - fixing link to `WorkfileSettings.set_context_settings` to nuke launch callback --- pype/api.py | 2 + pype/nuke/__init__.py | 2 +- pype/nuke/lib.py | 297 ++++++++++++++++++++++++++++++++++++------ pype/nuke/menu.py | 10 +- 4 files changed, 272 insertions(+), 39 deletions(-) diff --git a/pype/api.py b/pype/api.py index 2c626f36a3..2c227b5b4b 100644 --- a/pype/api.py +++ b/pype/api.py @@ -23,6 +23,7 @@ from .lib import ( get_asset, get_project, get_hierarchy, + get_subsets, get_version_from_path, modified_environ, add_tool_to_environment @@ -53,6 +54,7 @@ __all__ = [ "get_project", "get_hierarchy", "get_asset", + "get_subsets", "get_version_from_path", "modified_environ", "add_tool_to_environment", diff --git a/pype/nuke/__init__.py b/pype/nuke/__init__.py index 997a423d79..42ca633e40 100644 --- a/pype/nuke/__init__.py +++ b/pype/nuke/__init__.py @@ -104,7 +104,7 @@ def install(): avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled) - workfile_settings = lib.Workfile_settings() + workfile_settings = lib.WorkfileSettings() # Disable all families except for the ones we explicitly want to see family_states = [ "write", diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 0f30e8f76b..a43a3d32a5 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -380,7 +380,7 @@ def add_rendering_knobs(node): return node -class Workfile_settings(object): +class WorkfileSettings(object): """ All settings for workfile will be set @@ -394,23 +394,39 @@ class Workfile_settings(object): nodes_filter (list): filtering classes for nodes """ - def __init__(self, asset=None, root_node=None, nodes=None, nodes_filter=[]): - self._project = io.find_one({"type": "project"}) - self._asset = asset or api.Session["AVALON_ASSET"] + + def __init__(self, + root_node=None, + nodes=None, + **kwargs): + self._project = kwargs.get( + "project") or io.find_one({"type": "project"}) + self._asset = kwargs.get("asset_name") or api.Session["AVALON_ASSET"] self._asset_entity = pype.get_asset(self._asset) self._root_node = root_node or nuke.root() - self._nodes = nodes or [] - self._nodes_filter = nodes_filter + self._nodes = self.get_nodes(nodes=nodes) - def get_nodes(self): - if (self._nodes is []) and (self._nodes_filter is not []): - for filter in self._nodes_filter: - self._nodes += nuke.allNodes(filter=filter) - elif (self._nodes is not []) and (self._nodes_filter is not []): - for filter in self._nodes_filter: - self._nodes += [n for n in self._nodes if filter in n.Class()] - elif (self._nodes is []) and (self._nodes_filter is []): - self._nodes += [n for n in nuke.allNodes()] + self.data = kwargs + + def get_nodes(self, nodes=None, nodes_filter=None): + # filter out only dictionaries for node creation + # + # print("\n\n") + # pprint(self._nodes) + # + + if not isinstance(nodes, list) and not isinstance(nodes_filter, list): + return [n for n in nuke.allNodes()] + elif not isinstance(nodes, list) and isinstance(nodes_filter, list): + nodes = list() + for filter in nodes_filter: + [nodes.append(n) for n in nuke.allNodes(filter=filter)] + return nodes + elif isinstance(nodes, list) and not isinstance(nodes_filter, list): + return [n for n in self._nodes] + elif isinstance(nodes, list) and isinstance(nodes_filter, list): + for filter in nodes_filter: + return [n for n in self._nodes if filter in n.Class()] def set_viewers_colorspace(self, viewer_dict): ''' Adds correct colorspace to viewer @@ -428,7 +444,8 @@ class Workfile_settings(object): ] erased_viewers = [] - for v in [n for n in self._nodes if "Viewer" in n.Class()]: + for v in [n for n in self._nodes + if "Viewer" in n.Class()]: v['viewerProcess'].setValue(str(viewer_dict["viewerProcess"])) if str(viewer_dict["viewerProcess"]) \ not in v['viewerProcess'].value(): @@ -560,7 +577,7 @@ class Workfile_settings(object): if len(missing_cols) > 0: missing = ", ".join(missing_cols) msg = "'{}' are not set for asset '{}'!".format( - missing, self._asset) + missing, self._asset) log.warning(msg) nuke.message(msg) return @@ -612,7 +629,7 @@ class Workfile_settings(object): log.error("Missing set shot attributes in DB. \nContact" "your supervisor!. \n\nWidth: `{0}` \nHeight: `{1}`" "\nPixel Asspect: `{2}`".format( - width, height, pixel_aspect)) + width, height, pixel_aspect)) return bbox = self._asset_entity.get('data', {}).get('crop') @@ -772,23 +789,72 @@ def get_write_node_template_attr(node): return avalon.nuke.lib.fix_data_for_node_create(correct_data) -class Build_Workfile(Workfile_settings): - def __init__(self, root=None, asset_name=None, task=None, hierarchy=None, version=1): - Workfile_settings.__init__(self, asset=asset_name) +class BuildWorkfile(WorkfileSettings): + """ + Building first version of workfile. - ### create workfile path - # get project from database - project = self._project or io.find_one({"type": "project"}) + Settings are taken from presets and db. It will add all subsets in last version for defined representaions + Arguments: + variable (type): description + + """ + xpos = 0 + ypos = 0 + xpos_size = 80 + ypos_size = 90 + xpos_gap = 50 + ypos_gap = 50 + + def __init__(self, + root_path=None, + root_node=None, + nodes=None, + nodes_effects=None, + **kwargs): + """ + A short description. + + A bit longer description. + + Argumetns: + root_path (str): description + root_node (nuke.Node): description + nodes (list): list of nuke.Node + nodes_effects (dict): dictionary with subsets + + Example: + nodes_effects = { + "plateMain": { + "nodes": [ + [("Class", "Reformat"), + ("resize", "distort"), + ("flip", True)], + + [("Class", "Grade"), + ("blackpoint", 0.5), + ("multiply", 0.4)] + ] + }, + } + + """ + + WorkfileSettings.__init__(self, + root_node=root_node, + nodes=nodes, + **kwargs) + + self._nodes_read_effects = nodes_effects or {} # collect data for formating data = { - "root": root or api.Session["AVALON_PROJECTS"], - "project": {"name": project["name"], - "code": project["data"].get("code", '')}, - "asset": asset_name or os.environ["AVALON_ASSET"], - "task": task or api.Session["AVALON_TASK"].lower(), - "hierarchy": hierarchy or pype.get_hierarchy(), - "version": version, + "root": root_path or api.Session["AVALON_PROJECTS"], + "project": {"name": self._project["name"], + "code": self._project["data"].get("code", '')}, + "asset": self._asset or os.environ["AVALON_ASSET"], + "task": kwargs.get("task") or api.Session["AVALON_TASK"].lower(), + "hierarchy": kwargs.get("hierarchy") or pype.get_hierarchy(), + "version": kwargs.get("version", {}).get("name", 1), "user": getpass.getuser(), "comment": "firstBuild" } @@ -799,11 +865,168 @@ class Build_Workfile(Workfile_settings): anatomy_filled = anatomy.format(data) # get dir and file for workfile - templ_work_dir = anatomy_filled["avalon"]["work"] - templ_work_file = anatomy_filled["avalon"]["workfile"] + ".nk" + self.work_dir = anatomy_filled["avalon"]["work"] + self.work_file = anatomy_filled["avalon"]["workfile"] + ".nk" - # save script as - path = os.path.join(templ_work_dir, templ_work_file).replace("\\", "/") + def save_script_as(self, path=None): + # first clear anything in open window + nuke.scriptClear() - - print(path) + if not path: + dir = self.work_dir + path = os.path.join( + self.work_dir, + self.work_file).replace("\\", "/") + else: + dir = os.path.dirname(path) + + # check if folder is created + if not os.path.exists(dir): + os.makedirs(dir) + + # save script to path + nuke.scriptSaveAs(path) + + def process(self, + regex_filter=None, + version="last", + representations=["exr", "dpx"]): + """ + A short description. + + A bit longer description. + + Args: + variable (type): description + + Returns: + type: description + + Raises: + Exception: description + + """ + + # save the script + self.save_script_as() + + # create viewer and reset frame range + vn = nuke.createNode("Viewer") + vn["xpos"].setValue(self.xpos) + vn["ypos"].setValue(self.ypos) + + # move position + self.position_up() + + wn = self.write_create() + wn["xpos"].setValue(self.xpos) + wn["ypos"].setValue(self.ypos) + wn["render"].setValue(True) + vn.setInput(0, wn) + + # move position + self.position_up() + + # set frame range for new viewer + self.reset_frame_range_handles() + + # get all available representations + subsets = pype.get_subsets(self._asset, + regex_filter=regex_filter, + version=version, + representations=representations) + + for name, subset in subsets.items(): + log.info("Building Loader to: `{}`".format(name)) + version = subset["version"] + log.info("Version to: `{}`".format(version["name"])) + representations = subset["representaions"] + for repr in representations: + rn = self.read_loader(repr) + rn["xpos"].setValue(self.xpos) + rn["ypos"].setValue(self.ypos) + wn.setInput(0, rn) + + # get editional nodes + nodes = self._nodes_read_effects.get(name, {}).get("nodes", []) + + print("\n\n__________ nodes __________") + # create all editional nodes + for n in nodes: + print(n) + # create nodes + klass, value = n[0] + node = nuke.createNode(value) + print(node.name()) + + for k, v in n: + if "Class" not in k: + node[k].setValue(v) + self._nodes.append(node) + + # move position + self.position_right() + + def read_loader(self, representation): + """ + Gets Loader plugin for image sequence or mov + + Arguments: + representation (dict): avalon db entity + + """ + context = representation["context"] + read_name = "Read_{0}_{1}_{2}".format(context["asset"], + context["subset"], + context["representation"]) + + loader_name = "LoadSequence" + if "mov" in context["representation"]: + loader_name = "LoadMov" + + loader_plugin = None + for Loader in api.discover(api.Loader): + if Loader.__name__ != loader_name: + continue + + loader_plugin = Loader + + return api.load(Loader=loader_plugin, + representation=representation["_id"]) + + def write_create(self): + """ + Create render write + + Arguments: + representation (dict): avalon db entity + + """ + + Create_name = "CreateWriteRender" + + creator_plugin = None + for Creator in api.discover(api.Creator): + if Creator.__name__ != Create_name: + continue + + creator_plugin = Creator + + # return api.create() + return creator_plugin("render_writeMain", self._asset).process() + + def position_reset(self): + self.xpos = 0 + self.ypos = 0 + + def position_right(self): + self.xpos += self.xpos_size + self.xpos_gap + + def position_left(self): + self.xpos -= self.xpos_size + self.xpos_gap + + def position_down(self): + self.ypos -= self.ypos_size + self.ypos_gap + + def position_up(self): + self.ypos -= self.ypos_size + self.ypos_gap diff --git a/pype/nuke/menu.py b/pype/nuke/menu.py index 4d57f322e6..56111674a8 100644 --- a/pype/nuke/menu.py +++ b/pype/nuke/menu.py @@ -9,7 +9,7 @@ log = Logger().get_logger(__name__, "nuke") def install(): menubar = nuke.menu("Nuke") menu = menubar.findItem(Session["AVALON_LABEL"]) - workfile_settings = lib.Workfile_settings() + workfile_settings = lib.WorkfileSettings() # replace reset resolution from avalon core to pype's name = "Reset Resolution" new_name = "Set Resolution" @@ -41,6 +41,14 @@ def install(): ) log.debug("Adding menu item: {}".format(name)) + # add workfile builder menu item + name = "Build First Workfile.." + menu.addCommand( + name, lib.BuildWorkfile().process, + index=(rm_item[0]+7) + ) + log.debug("Adding menu item: {}".format(name)) + # add item that applies all setting above name = "Apply all settings" menu.addCommand( From e10cc6449880a6f52bfe4b51a58831de599748a6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 15 Aug 2019 10:43:04 +0200 Subject: [PATCH 12/26] feat(nks): adding nuke `BuildWorkfile` to be used by nukestudio during publishing --- pype/nukestudio/lib.py | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index 6674e8a3aa..be9cc99ddd 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -287,3 +287,58 @@ def _show_no_gui(): messagebox.setStandardButtons(messagebox.Ok) messagebox.exec_() + +def CreateNukeWorkfile(nodes=None, + to_timeline=False, + **kwargs): + ''' Creating nuke workfile with particular version with given nodes + Also it is creating timeline track items as precomps. + + Arguments: + nodes(list of dict): each key in dict is knob order is important + to_timeline(type): will build trackItem with metadata + + Returns: + bool: True if done + + Raises: + Exception: with traceback + + ''' + import hiero.core + from avalon.nuke import imprint + from pype.nuke import ( + lib as nklib + ) + + # check if the file exists if does then Raise "File exists!" + if os.path.exists(filepath): + raise FileExistsError("File already exists: `{}`".format(filepath)) + + # if no representations matching then + # Raise "no representations to be build" + if len(representations) == 0: + raise AttributeError("Missing list of `representations`") + + # check nodes input + if len(nodes) == 0: + log.warning("Missing list of `nodes`") + + # create temp nk file + nuke_script = hiero.core.nuke.ScriptWriter() + + # create root node and save all metadata + root_node = hiero.core.nuke.RootNode() + + root_path = os.environ["AVALON_PROJECTS"] + + nuke_script.addNode(root_node) + + # here to call pype.nuke.lib.BuildWorkfile + script_builder = nklib.BuildWorkfile( + nuke_script=nuke_script, + root_node=root_node, + root_path=root_path, + nodes=nodes, + **kwargs + ) From e58689b23275336fd645fbf482514fed0336d264 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 15 Aug 2019 10:46:55 +0200 Subject: [PATCH 13/26] fix(nk): create write was not returning the node back --- pype/plugins/nuke/create/create_write.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/plugins/nuke/create/create_write.py b/pype/plugins/nuke/create/create_write.py index 03107238b5..c3da555259 100644 --- a/pype/plugins/nuke/create/create_write.py +++ b/pype/plugins/nuke/create/create_write.py @@ -69,9 +69,7 @@ class CreateWriteRender(avalon.nuke.Creator): write_data.update({ "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}"}) - create_write_node(self.data["subset"], write_data) - - return + return create_write_node(self.data["subset"], write_data) class CreateWritePrerender(avalon.nuke.Creator): From 250a5f67419152f9a9261339489a9e46f3237301 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 15 Aug 2019 10:47:31 +0200 Subject: [PATCH 14/26] fix(nk): changing the name of Read loaded node to be more unique --- pype/plugins/nuke/load/load_sequence.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/plugins/nuke/load/load_sequence.py b/pype/plugins/nuke/load/load_sequence.py index 5fd43d3481..471dfcf555 100644 --- a/pype/plugins/nuke/load/load_sequence.py +++ b/pype/plugins/nuke/load/load_sequence.py @@ -107,7 +107,11 @@ class LoadSequence(api.Loader): file = self.fname.replace("\\", "/") log.info("file: {}\n".format(self.fname)) - read_name = "Read_" + context["representation"]["context"]["subset"] + repr_cont = context["representation"]["context"] + read_name = "Read_{0}_{1}_{2}".format( + repr_cont["asset"], + repr_cont["subset"], + repr_cont["representation"]) # Create the Loader with the filename path set with viewer_update_and_undo_stop(): @@ -227,7 +231,7 @@ class LoadSequence(api.Loader): self.first_frame = int(nuke.root()["first_frame"].getValue()) self.handle_start = version_data.get("handleStart", 0) - + first = version_data.get("frameStart", None) last = version_data.get("frameEnd", None) From c87dca6f9d2eba3f6675b42ce16b2926ffe7f632 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 15 Aug 2019 17:09:34 +0200 Subject: [PATCH 15/26] fix(nk, nks): version is better if None then "last" --- pype/lib.py | 5 +++-- pype/nuke/lib.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pype/lib.py b/pype/lib.py index c926601a14..12ec1f5584 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -495,7 +495,7 @@ def filter_pyblish_plugins(plugins): def get_subsets(asset_name, regex_filter=None, - version="last", + version=None, representations=["exr", "dpx"]): """ Query subsets with filter on name. @@ -538,11 +538,12 @@ def get_subsets(asset_name, output_dict = {} # Process subsets for subset in subsets: - if "last" in str(version): + if not version: version_sel = io.find_one({"type": "version", "parent": subset["_id"]}, sort=[("name", -1)]) else: + assert isinstance(version, int), "version needs to be `int` type" version_sel = io.find_one({"type": "version", "parent": subset["_id"], "name": int(version)}) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index a43a3d32a5..1eae684b6f 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -889,7 +889,7 @@ class BuildWorkfile(WorkfileSettings): def process(self, regex_filter=None, - version="last", + version=None, representations=["exr", "dpx"]): """ A short description. @@ -897,7 +897,9 @@ class BuildWorkfile(WorkfileSettings): A bit longer description. Args: - variable (type): description + regex_filter (raw string): regex pattern to filter out subsets + version (int): define a particular version, None gets last + representations (list): Returns: type: description From 1a3b02d65ef10b5d68fa4c1f1faa88828e2131cf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 16 Aug 2019 09:33:43 +0200 Subject: [PATCH 16/26] feat(nk): adding `backdrop` wrapper to `BuildWorkfile` --- pype/nuke/lib.py | 134 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 37 deletions(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 1eae684b6f..383da52d0c 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -197,6 +197,7 @@ def script_name(): ''' return nuke.root().knob('name').value() + def add_button_write_to_read(node): name = "createReadNode" label = "Create Read" @@ -205,6 +206,7 @@ def add_button_write_to_read(node): k.setFlag(0x1000) node.addKnob(k) + def create_write_node(name, data, prenodes=None): ''' Creating write node which is group node @@ -313,7 +315,6 @@ def create_write_node(name, data, prenodes=None): else: prev_node = nuke.createNode("Input", "name rgba") - # creating write node now_node = avalon.nuke.lib.add_write_node("inside_{}".format(name), **_data @@ -333,7 +334,6 @@ def create_write_node(name, data, prenodes=None): # imprinting group node GN = avalon.nuke.imprint(GN, data["avalon"]) - divider = nuke.Text_Knob('') GN.addKnob(divider) @@ -349,7 +349,6 @@ def create_write_node(name, data, prenodes=None): tile_color = _data.get("tile_color", "0xff0000ff") GN["tile_color"].setValue(tile_color) - # add render button lnk = nuke.Link_Knob("Render") lnk.makeLink(write_node.name(), "Render") @@ -805,12 +804,13 @@ class BuildWorkfile(WorkfileSettings): ypos_size = 90 xpos_gap = 50 ypos_gap = 50 + pos_layer = 10 def __init__(self, root_path=None, root_node=None, nodes=None, - nodes_effects=None, + to_script=None, **kwargs): """ A short description. @@ -844,8 +844,7 @@ class BuildWorkfile(WorkfileSettings): root_node=root_node, nodes=nodes, **kwargs) - - self._nodes_read_effects = nodes_effects or {} + self.to_script = to_script # collect data for formating data = { "root": root_path or api.Session["AVALON_PROJECTS"], @@ -899,7 +898,7 @@ class BuildWorkfile(WorkfileSettings): Args: regex_filter (raw string): regex pattern to filter out subsets version (int): define a particular version, None gets last - representations (list): + representations (list): Returns: type: description @@ -909,13 +908,18 @@ class BuildWorkfile(WorkfileSettings): """ - # save the script - self.save_script_as() + if not self.to_script: + # save the script + self.save_script_as() # create viewer and reset frame range - vn = nuke.createNode("Viewer") - vn["xpos"].setValue(self.xpos) - vn["ypos"].setValue(self.ypos) + viewer = self.get_nodes(nodes_filter=["Viewer"]) + if not viewer: + vn = nuke.createNode("Viewer") + vn["xpos"].setValue(self.xpos) + vn["ypos"].setValue(self.ypos) + else: + vn = viewer[-1] # move position self.position_up() @@ -926,8 +930,12 @@ class BuildWorkfile(WorkfileSettings): wn["render"].setValue(True) vn.setInput(0, wn) + bdn = self.create_backdrop(label="Render write \n\n\n\nOUTPUT", + color='0xcc1102ff', layer=-1, + nodes=[wn]) + # move position - self.position_up() + self.position_up(2) # set frame range for new viewer self.reset_frame_range_handles() @@ -938,6 +946,7 @@ class BuildWorkfile(WorkfileSettings): version=version, representations=representations) + nodes_backdrop = list() for name, subset in subsets.items(): log.info("Building Loader to: `{}`".format(name)) version = subset["version"] @@ -950,24 +959,30 @@ class BuildWorkfile(WorkfileSettings): wn.setInput(0, rn) # get editional nodes - nodes = self._nodes_read_effects.get(name, {}).get("nodes", []) + # # TODO: link it to nut Create and Load print("\n\n__________ nodes __________") - # create all editional nodes - for n in nodes: - print(n) - # create nodes - klass, value = n[0] - node = nuke.createNode(value) - print(node.name()) - - for k, v in n: - if "Class" not in k: - node[k].setValue(v) - self._nodes.append(node) + # # create all editional nodes + # for n in nodes: + # print(n) + # # create nodes + # klass, value = n[0] + # node = nuke.createNode(value) + # print(node.name()) + # + # for k, v in n: + # if "Class" not in k: + # node[k].setValue(v) + # self._nodes.append(node) # move position self.position_right() + nodes_backdrop.append(rn) + + + bdn = self.create_backdrop(label="Loaded Reads", + color='0x2d7702ff', layer=-1, + nodes=nodes_backdrop) def read_loader(self, representation): """ @@ -1017,18 +1032,63 @@ class BuildWorkfile(WorkfileSettings): # return api.create() return creator_plugin("render_writeMain", self._asset).process() - def position_reset(self): - self.xpos = 0 - self.ypos = 0 + def create_backdrop(self, label="", color=None, layer=0, + nodes=None): + """ + Create Backdrop node - def position_right(self): - self.xpos += self.xpos_size + self.xpos_gap + Arguments: + color (str): nuke compatible string with color code + layer (int): layer of node usually used (self.pos_layer - 1) + label (str): the message + nodes (list): list of nodes to be wrapped into backdrop - def position_left(self): - self.xpos -= self.xpos_size + self.xpos_gap + """ + assert isinstance(nodes, list), "`nodes` should be a list of nodes" - def position_down(self): - self.ypos -= self.ypos_size + self.ypos_gap + # Calculate bounds for the backdrop node. + bdX = min([node.xpos() for node in nodes]) + bdY = min([node.ypos() for node in nodes]) + bdW = max([node.xpos() + node.screenWidth() for node in nodes]) - bdX + bdH = max([node.ypos() + node.screenHeight() for node in nodes]) - bdY - def position_up(self): - self.ypos -= self.ypos_size + self.ypos_gap + # Expand the bounds to leave a little border. Elements are offsets + # for left, top, right and bottom edges respectively + left, top, right, bottom = (-20, -65, 20, 60) + bdX += left + bdY += top + bdW += (right - left) + bdH += (bottom - top) + + bdn = nuke.createNode("BackdropNode") + bdn["z_order"].setValue(self.pos_layer + layer) + + if color: + bdn["tile_color"].setValue(int(color, 16)) + + bdn["xpos"].setValue(bdX) + bdn["ypos"].setValue(bdY) + bdn["bdwidth"].setValue(bdW) + bdn["bdheight"].setValue(bdH) + + if label: + bdn["label"].setValue(label) + + bdn["note_font_size"].setValue(20) + return bdn + + def position_reset(self, xpos=0, ypos=0): + self.xpos = xpos + self.ypos = ypos + + def position_right(self, multiply=1): + self.xpos += (self.xpos_size * multiply) + self.xpos_gap + + def position_left(self, multiply=1): + self.xpos -= (self.xpos_size * multiply) + self.xpos_gap + + def position_down(self, multiply=1): + self.ypos -= (self.ypos_size * multiply) + self.ypos_gap + + def position_up(self, multiply=1): + self.ypos -= (self.ypos_size * multiply) + self.ypos_gap From ab3798d01dfa50f9615a1dd12045222dc1329ede Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 16 Aug 2019 09:34:12 +0200 Subject: [PATCH 17/26] fix(nks): removing redundant attributes --- pype/nukestudio/lib.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index be9cc99ddd..81b48f294d 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -189,7 +189,7 @@ def add_submission(): class PublishAction(QtWidgets.QAction): """ - Action with is showing as menu item + Action with is showing as menu item """ def __init__(self): @@ -288,7 +288,9 @@ def _show_no_gui(): messagebox.setStandardButtons(messagebox.Ok) messagebox.exec_() + def CreateNukeWorkfile(nodes=None, + nodes_effects=None, to_timeline=False, **kwargs): ''' Creating nuke workfile with particular version with given nodes @@ -336,9 +338,8 @@ def CreateNukeWorkfile(nodes=None, # here to call pype.nuke.lib.BuildWorkfile script_builder = nklib.BuildWorkfile( - nuke_script=nuke_script, root_node=root_node, root_path=root_path, - nodes=nodes, + nodes=nuke_script.getNodes(), **kwargs ) From d1cf46575cefca3b0a3e4c373d8cc70683d86fc0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 16 Aug 2019 18:03:00 +0200 Subject: [PATCH 18/26] feat(nks): adding collection of subTrackItems to collect Clips --- .../nukestudio/publish/collect_clips.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index da71e2ab50..f6b0da1bca 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -20,7 +20,35 @@ class CollectClips(api.ContextPlugin): projectdata = context.data["projectData"] version = context.data.get("version", "001") + sequence = context.data.get("activeSequence") instances_data = [] + + # get all subTrackItems and add it to context + effects_on_tracks = [] + sub_track_items = [] + # looop trough all tracks and search for subtreacks + for track_index, video_track in enumerate(sequence.videoTracks()): + sub_items = video_track.subTrackItems() + items = video_track.items() + if not sub_items: + continue + for si in sub_items: + selected_track = [(indx, vt) for indx, vt in enumerate(sequence.videoTracks()) + if vt.name() in si[0].parentTrack().name()] + + # if filtered track index is the same as \ + # actual track there is match + if (selected_track[0][0] == track_index): + sub_track_items += si + if (track_index not in effects_on_tracks): + effects_on_tracks.append(track_index) + + # self.log.debug("_>_ effects_on_tracks `{}`".format(effects_on_tracks)) + # self.log.debug("_|_ sub_track_items `{}`".format(sub_track_items)) + # add it to context + context.data["subTrackUsedTracks"] = effects_on_tracks + context.data["subTrackItems"] = sub_track_items + for item in context.data.get("selection", []): # Skip audio track items # Try/Except is to handle items types, like EffectTrackItem From 6162a519a5b4da7a983b8782f7f4f2114889b880 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 16 Aug 2019 18:03:22 +0200 Subject: [PATCH 19/26] feat(nks): collecting effects to instances --- .../nukestudio/publish/collect_effects.py | 111 +++++++++++++----- .../nukestudio/publish/extract_effects.py | 20 ++++ 2 files changed, 101 insertions(+), 30 deletions(-) create mode 100644 pype/plugins/nukestudio/publish/extract_effects.py diff --git a/pype/plugins/nukestudio/publish/collect_effects.py b/pype/plugins/nukestudio/publish/collect_effects.py index 7643f1810e..07367b31ca 100644 --- a/pype/plugins/nukestudio/publish/collect_effects.py +++ b/pype/plugins/nukestudio/publish/collect_effects.py @@ -2,15 +2,26 @@ import pyblish.api import hiero.core -class CollectVideoTracksEffects(pyblish.api.ContextPlugin): +class CollectVideoTracksEffects(pyblish.api.InstancePlugin): """Collect video tracks effects into context.""" - order = pyblish.api.CollectorOrder + 0.1 + order = pyblish.api.CollectorOrder + 0.1015 label = "Effects from video tracks" - def process(self, context): + def process(self, instance): + + self.log.debug("Finding soft effect for subset: `{}`".format(instance.data.get("subset"))) + # taking active sequence - sequence = context.data['activeSequence'] + sequence = instance.context.data['activeSequence'] + effects_on_tracks = instance.context.data.get("subTrackUsedTracks") + sub_track_items = instance.context.data.get("subTrackItems") + track = instance.data["track"] + + timeline_in_h = instance.data["clipInH"] + timeline_out_h = instance.data["clipOutH"] + timeline_in = instance.data["clipIn"] + timeline_out = instance.data["clipOut"] # adding ignoring knob keys _ignoring_keys = ['invert_mask', 'help', 'mask', @@ -21,31 +32,71 @@ class CollectVideoTracksEffects(pyblish.api.ContextPlugin): 'select_cccid', 'mix', 'version'] # creating context attribute - context.data["effectTrackItems"] = effects = dict() + effects = dict() - # loop trough all videotracks - for track_index, video_track in enumerate(sequence.videoTracks()): - # loop trough all available subtracks - for subtrack_item in video_track.subTrackItems(): - # ignore anything not EffectTrackItem - if isinstance(subtrack_item[0], hiero.core.EffectTrackItem): - et_item = subtrack_item[0] - # ignore item if not enabled - if et_item.isEnabled(): - node = et_item.node() - node_serialized = {} - # loop trough all knobs and collect not ignored - # and any with any value - for knob in node.knobs().keys(): - if (knob not in _ignoring_keys) and node[knob].value(): - node_serialized[knob] = node[knob].value() - # add it to the context attribute - effects.update({et_item.name(): { - "timelineIn": int(et_item.timelineIn()), - "timelineOut": int(et_item.timelineOut()), - "subTrackIndex": et_item.subTrackIndex(), - "trackIndex": track_index, - "node": node_serialized - }}) - self.log.debug("effects: {}".format(effects)) + for subtrack_item in sub_track_items: + sub_track = subtrack_item.parentTrack().name() + # ignore anything not EffectTrackItem + if isinstance(subtrack_item, hiero.core.EffectTrackItem): + et_item = subtrack_item + # ignore item if not enabled + if et_item.isEnabled(): + node = et_item.node() + node_serialized = {} + # loop trough all knobs and collect not ignored + # and any with any value + for knob in node.knobs().keys(): + # skip nodes in ignore keys + if knob in _ignoring_keys: + continue + + # get animation if node is animated + if node[knob].isAnimated(): + # grab animation including handles + knob_anim = [node[knob].getValueAt(i) + for i in range(timeline_in_h, timeline_out_h + 1)] + + node_serialized[knob] = knob_anim + else: + node_serialized[knob] = node[knob].value() + + # pick track index from subTrackItem + pick_sub_track = [indx for indx, vt in enumerate(sequence.videoTracks()) + if vt.name() in sub_track] + # pick track index from trackItem + pick_track = [indx for indx, vt in enumerate(sequence.videoTracks()) + if vt.name() in track] + # collect timelineIn/Out + effect_t_in = int(et_item.timelineIn()) + effect_t_out = int(et_item.timelineOut()) + + # controle if parent track has video trackItems + items_check = et_item.parent().items() + + # filter out all track items under any track with effects + # also filter out track item bellow + if (pick_track[0] in effects_on_tracks) and (pick_sub_track[0] >= pick_track[0]): + if (effect_t_in == timeline_in) and (effect_t_out == timeline_out): + effects.update({et_item.name(): { + "timelineIn": effect_t_in, + "timelineOut": effect_t_out, + "subTrackIndex": et_item.subTrackIndex(), + "trackIndex": pick_track[0], + # "node": node_serialized + }}) + # for subTrackItem on track without any trackItems + elif len(items_check) == 0: + effects.update({et_item.name(): { + "timelineIn": effect_t_in, + "timelineOut": effect_t_out, + "subTrackIndex": et_item.subTrackIndex(), + "trackIndex": pick_track[0], + "node": node_serialized + }}) + + instance.data["effectTrackItems"] = effects + if len(instance.data.get("effectTrackItems", {}).keys()) > 0: + instance.data["families"] += ["effects"] + self.log.debug("effects.keys: {}".format(instance.data.get("effectTrackItems", {}).keys())) + self.log.debug("effects: {}".format(instance.data.get("effectTrackItems", {}))) diff --git a/pype/plugins/nukestudio/publish/extract_effects.py b/pype/plugins/nukestudio/publish/extract_effects.py new file mode 100644 index 0000000000..5cf1ab2ff1 --- /dev/null +++ b/pype/plugins/nukestudio/publish/extract_effects.py @@ -0,0 +1,20 @@ +import pyblish.api +import os +import json + + +class ExtractVideoTracksEffects(pyblish.api.InstancePlugin): + """Collect video tracks effects into context.""" + + order = pyblish.api.CollectorOrder + 0.1018 + label = "Export Effects" + femilies = ["effects"] + + def process(self, instance): + effects = instance.data.get("effectTrackItems") + subset = instance.data.get("subset") + + if effects: + self.log.info("_ subset: `{}`".format(subset)) + for ef in effects.keys(): + self.log.info("_ ef: `{}`".format(ef)) From f2152d64f1883bd121cb6318b314b93a79a71e44 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Aug 2019 15:34:12 +0200 Subject: [PATCH 20/26] fix(nks): desabling collect metadata as it is not finished yet --- .../nukestudio/publish/collect_metadata.py | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 pype/plugins/nukestudio/publish/collect_metadata.py diff --git a/pype/plugins/nukestudio/publish/collect_metadata.py b/pype/plugins/nukestudio/publish/collect_metadata.py deleted file mode 100644 index 23d36ba4a2..0000000000 --- a/pype/plugins/nukestudio/publish/collect_metadata.py +++ /dev/null @@ -1,30 +0,0 @@ -from pyblish import api - - -class CollectClipMetadata(api.InstancePlugin): - """Collect Metadata from selected track items.""" - - order = api.CollectorOrder + 0.01 - label = "Collect Metadata" - hosts = ["nukestudio"] - - def process(self, instance): - item = instance.data["item"] - ti_metadata = self.metadata_to_string(dict(item.metadata())) - ms_metadata = self.metadata_to_string( - dict(item.source().mediaSource().metadata())) - - instance.data["clipMetadata"] = ti_metadata - instance.data["mediaSourceMetadata"] = ms_metadata - - self.log.info(instance.data["clipMetadata"]) - self.log.info(instance.data["mediaSourceMetadata"]) - return - - def metadata_to_string(self, metadata): - data = dict() - for k, v in metadata.items(): - if v not in ["-", ""]: - data[str(k)] = v - - return data From 7c6833b2319c9e7829f7dcd165281b5ff807fdd8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Aug 2019 15:34:45 +0200 Subject: [PATCH 21/26] fix(nks): extracting is now with collect destinations --- .../nukestudio/publish/extract_effects.py | 233 +++++++++++++++++- 1 file changed, 222 insertions(+), 11 deletions(-) diff --git a/pype/plugins/nukestudio/publish/extract_effects.py b/pype/plugins/nukestudio/publish/extract_effects.py index 5cf1ab2ff1..5e2d95d943 100644 --- a/pype/plugins/nukestudio/publish/extract_effects.py +++ b/pype/plugins/nukestudio/publish/extract_effects.py @@ -1,20 +1,231 @@ -import pyblish.api +# from pype import plugins import os import json +import re +import pyblish.api +import tempfile +from avalon import io, api - -class ExtractVideoTracksEffects(pyblish.api.InstancePlugin): +class ExtractVideoTracksLuts(pyblish.api.InstancePlugin): """Collect video tracks effects into context.""" - order = pyblish.api.CollectorOrder + 0.1018 - label = "Export Effects" - femilies = ["effects"] + order = pyblish.api.ExtractorOrder + label = "Export Soft Lut Effects" + families = ["lut"] def process(self, instance): + item = instance.data["item"] effects = instance.data.get("effectTrackItems") - subset = instance.data.get("subset") - if effects: - self.log.info("_ subset: `{}`".format(subset)) - for ef in effects.keys(): - self.log.info("_ ef: `{}`".format(ef)) + instance.data["families"] = [f for f in instance.data.get("families", []) if f not in ["lut"]] + + self.log.debug("___ instance.data[families]: `{}`".format(instance.data["families"])) + + # skip any without effects + if not effects: + return + + subset = instance.data.get("subset") + subset_split = re.findall(r'[A-Z][^A-Z]*', subset) + + if len(subset_split) > 0: + root_name = subset.replace(subset_split[0], "") + subset_split.insert(0, root_name.capitalize()) + + subset_split.insert(0, "lut") + + self.log.debug("creating staging dir") + # staging_dir = self.staging_dir(instance) + + # TODO: only provisory will be replace by function + staging_dir = instance.data.get('stagingDir', None) + + if not staging_dir: + staging_dir = os.path.normpath( + tempfile.mkdtemp(prefix="pyblish_tmp_") + ) + instance.data['stagingDir'] = staging_dir + + self.log.debug("creating staging dir: `{}`".format(staging_dir)) + + transfers = list() + if "transfers" not in instance.data: + instance.data["transfers"] = list() + + name = "".join(subset_split) + ext = "json" + file = name + "." + ext + + # create new instance and inherit data + data = {} + for key, value in instance.data.iteritems(): + data[key] = value + + # change names + data["subset"] = name + data["family"] = "lut" + data["families"] = [] + data["name"] = data["subset"] + "_" + data["asset"] + data["label"] = "{} - {} - ({})".format( + data['asset'], data["subset"], os.path.splitext(file)[1] + ) + data["source"] = data["sourcePath"] + + # create new instance + instance = instance.context.create_instance(**data) + + dst_dir = self.resource_destination_dir(instance) + + # change paths in effects to files + for k, effect in effects["effects"].items(): + trn = self.copy_linked_files(effect, dst_dir) + if trn: + transfers.append((trn[0], trn[1])) + + instance.data["transfers"].extend(transfers) + self.log.debug("_ transfers: `{}`".format( + instance.data["transfers"])) + + # create representations + instance.data["representations"] = list() + + transfer_data = [ + "handleStart", "handleEnd", "sourceIn", "sourceOut", + "frameStart", "frameEnd", "sourceInH", "sourceOutH", + "clipIn", "clipOut", "clipInH", "clipOutH", "asset", "track", + "version" + ] + + # pass data to version + version_data = dict() + version_data.update({k: instance.data[k] for k in transfer_data}) + + # add to data of representation + version_data.update({ + "handles": version_data['handleStart'], + "colorspace": item.sourceMediaColourTransform(), + "colorspaceScript": instance.context.data["colorspace"], + "families": ["plate", "lut"], + "subset": name, + "fps": instance.context.data["fps"] + }) + instance.data["versionData"] = version_data + + representation = { + 'files': file, + 'stagingDir': staging_dir, + 'name': "lut" + ext.title(), + 'ext': ext + } + instance.data["representations"].append(representation) + + self.log.debug("_ representations: `{}`".format( + instance.data["representations"])) + + self.log.debug("_ version_data: `{}`".format( + instance.data["versionData"])) + + with open(os.path.join(staging_dir, file), "w") as outfile: + outfile.write(json.dumps(effects, indent=4, sort_keys=True)) + + return + + def copy_linked_files(self, effect, dst_dir): + for k, v in effect["node"].items(): + if k in "file" and v is not '': + base_name = os.path.basename(v) + dst = os.path.join(dst_dir, base_name).replace("\\", "/") + + # add it to the json + effect["node"][k] = dst + return (v, dst) + + def resource_destination_dir(self, instance): + anatomy = instance.context.data['anatomy'] + self.create_destination_template(instance, anatomy) + + return os.path.join( + instance.data["assumedDestination"], + "resources" + ) + + def create_destination_template(self, instance, anatomy): + """Create a filepath based on the current data available + + Example template: + {root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/ + {subset}.{representation} + Args: + instance: the instance to publish + + Returns: + file path (str) + """ + + # get all the stuff from the database + subset_name = instance.data["subset"] + self.log.info(subset_name) + asset_name = instance.data["asset"] + project_name = api.Session["AVALON_PROJECT"] + a_template = anatomy.templates + + project = io.find_one({"type": "project", + "name": project_name}, + projection={"config": True, "data": True}) + + template = a_template['publish']['path'] + # anatomy = instance.context.data['anatomy'] + + asset = io.find_one({"type": "asset", + "name": asset_name, + "parent": project["_id"]}) + + assert asset, ("No asset found by the name '{}' " + "in project '{}'".format(asset_name, project_name)) + silo = asset['silo'] + + subset = io.find_one({"type": "subset", + "name": subset_name, + "parent": asset["_id"]}) + + # assume there is no version yet, we start at `1` + version = None + version_number = 1 + if subset is not None: + version = io.find_one({"type": "version", + "parent": subset["_id"]}, + sort=[("name", -1)]) + + # if there is a subset there ought to be version + if version is not None: + version_number += version["name"] + + if instance.data.get('version'): + version_number = int(instance.data.get('version')) + + padding = int(a_template['render']['padding']) + + hierarchy = asset['data']['parents'] + if hierarchy: + # hierarchy = os.path.sep.join(hierarchy) + hierarchy = "/".join(hierarchy) + + template_data = {"root": api.Session["AVALON_PROJECTS"], + "project": {"name": project_name, + "code": project['data']['code']}, + "silo": silo, + "family": instance.data['family'], + "asset": asset_name, + "subset": subset_name, + "frame": ('#' * padding), + "version": version_number, + "hierarchy": hierarchy, + "representation": "TEMP"} + + instance.data["assumedTemplateData"] = template_data + self.log.info(template_data) + instance.data["template"] = template + # We take the parent folder of representation 'filepath' + instance.data["assumedDestination"] = os.path.dirname( + anatomy.format(template_data)["publish"]["path"] + ) From e058913fc302d9f618845c3808e913b29bede5fe Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Aug 2019 15:35:22 +0200 Subject: [PATCH 22/26] fix(nks): collect shots was context plugin but it should be instance plugin --- .../nukestudio/publish/collect_shots.py | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_shots.py b/pype/plugins/nukestudio/publish/collect_shots.py index 9fc14536fb..c1fcf05b89 100644 --- a/pype/plugins/nukestudio/publish/collect_shots.py +++ b/pype/plugins/nukestudio/publish/collect_shots.py @@ -1,7 +1,7 @@ from pyblish import api -class CollectShots(api.ContextPlugin): +class CollectShots(api.InstancePlugin): """Collect Shot from Clip.""" # Run just before CollectClipSubsets @@ -10,39 +10,39 @@ class CollectShots(api.ContextPlugin): hosts = ["nukestudio"] families = ["clip"] - def process(self, context): - for instance in context[:]: - # Exclude non-tagged instances. - tagged = False - for tag in instance.data["tags"]: - if tag["name"].lower() == "hierarchy": - tagged = True + def process(self, instance): + self.log.debug( + "Skipping \"{}\" because its not tagged with " + "\"Hierarchy\"".format(instance)) + # Exclude non-tagged instances. + tagged = False + for tag in instance.data["tags"]: + if tag["name"].lower() == "hierarchy": + tagged = True - if not tagged: - self.log.debug( - "Skipping \"{}\" because its not tagged with " - "\"Hierarchy\"".format(instance) - ) - continue - - # Collect data. - data = {} - for key, value in instance.data.iteritems(): - data[key] = value - - data["family"] = "shot" - data["families"] = [] - - data["subset"] = data["family"] + "Main" - - data["name"] = data["subset"] + "_" + data["asset"] - - data["label"] = data["asset"] + " - " + data["subset"] + " - tasks: {} - assetbuilds: {}".format( - data["tasks"], [x["name"] for x in data.get("assetbuilds", [])] + if not tagged: + self.log.debug( + "Skipping \"{}\" because its not tagged with " + "\"Hierarchy\"".format(instance) ) + return - # Create instance. - self.log.debug("Creating instance with: {}".format(data["name"])) - instance.context.create_instance(**data) + # Collect data. + data = {} + for key, value in instance.data.iteritems(): + data[key] = value - self.log.debug("_ context: {}".format(context[:])) + data["family"] = "shot" + data["families"] = [] + + data["subset"] = data["family"] + "Main" + + data["name"] = data["subset"] + "_" + data["asset"] + + data["label"] = data["asset"] + " - " + data["subset"] + " - tasks: {} - assetbuilds: {}".format( + data["tasks"], [x["name"] for x in data.get("assetbuilds", [])] + ) + + # Create instance. + self.log.debug("Creating instance with: {}".format(data["name"])) + instance.context.create_instance(**data) From c6a3911f563ca130abd0cef7480050387813cee5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Aug 2019 15:36:34 +0200 Subject: [PATCH 23/26] fix(nks): collecting effects was renamed to Lut and also improved the data collection with `assignedTo` attribute --- .../nukestudio/publish/collect_effects.py | 119 +++++++++--------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_effects.py b/pype/plugins/nukestudio/publish/collect_effects.py index 07367b31ca..5dc9a9e262 100644 --- a/pype/plugins/nukestudio/publish/collect_effects.py +++ b/pype/plugins/nukestudio/publish/collect_effects.py @@ -2,17 +2,19 @@ import pyblish.api import hiero.core -class CollectVideoTracksEffects(pyblish.api.InstancePlugin): +class CollectVideoTracksLuts(pyblish.api.InstancePlugin): """Collect video tracks effects into context.""" order = pyblish.api.CollectorOrder + 0.1015 - label = "Effects from video tracks" + label = "Collect Soft Lut Effects" + families = ["clip"] def process(self, instance): self.log.debug("Finding soft effect for subset: `{}`".format(instance.data.get("subset"))) # taking active sequence + subset = instance.data["subset"] sequence = instance.context.data['activeSequence'] effects_on_tracks = instance.context.data.get("subTrackUsedTracks") sub_track_items = instance.context.data.get("subTrackItems") @@ -32,71 +34,76 @@ class CollectVideoTracksEffects(pyblish.api.InstancePlugin): 'select_cccid', 'mix', 'version'] # creating context attribute - effects = dict() - + effects = {"assignTo": subset, "effects": dict()} for subtrack_item in sub_track_items: sub_track = subtrack_item.parentTrack().name() + # ignore anything not EffectTrackItem - if isinstance(subtrack_item, hiero.core.EffectTrackItem): - et_item = subtrack_item - # ignore item if not enabled - if et_item.isEnabled(): - node = et_item.node() - node_serialized = {} - # loop trough all knobs and collect not ignored - # and any with any value - for knob in node.knobs().keys(): - # skip nodes in ignore keys - if knob in _ignoring_keys: - continue + if not isinstance(subtrack_item, hiero.core.EffectTrackItem): + continue + et_item = subtrack_item - # get animation if node is animated - if node[knob].isAnimated(): - # grab animation including handles - knob_anim = [node[knob].getValueAt(i) - for i in range(timeline_in_h, timeline_out_h + 1)] + # ignore item if not enabled + if not et_item.isEnabled(): + continue - node_serialized[knob] = knob_anim - else: - node_serialized[knob] = node[knob].value() + node = et_item.node() + node_serialized = {} + # loop trough all knobs and collect not ignored + # and any with any value + for knob in node.knobs().keys(): + # skip nodes in ignore keys + if knob in _ignoring_keys: + continue - # pick track index from subTrackItem - pick_sub_track = [indx for indx, vt in enumerate(sequence.videoTracks()) - if vt.name() in sub_track] - # pick track index from trackItem - pick_track = [indx for indx, vt in enumerate(sequence.videoTracks()) - if vt.name() in track] - # collect timelineIn/Out - effect_t_in = int(et_item.timelineIn()) - effect_t_out = int(et_item.timelineOut()) + # get animation if node is animated + if node[knob].isAnimated(): + # grab animation including handles + knob_anim = [node[knob].getValueAt(i) + for i in range(timeline_in_h, timeline_out_h + 1)] - # controle if parent track has video trackItems - items_check = et_item.parent().items() + node_serialized[knob] = knob_anim + else: + node_serialized[knob] = node[knob].value() - # filter out all track items under any track with effects - # also filter out track item bellow - if (pick_track[0] in effects_on_tracks) and (pick_sub_track[0] >= pick_track[0]): - if (effect_t_in == timeline_in) and (effect_t_out == timeline_out): - effects.update({et_item.name(): { - "timelineIn": effect_t_in, - "timelineOut": effect_t_out, - "subTrackIndex": et_item.subTrackIndex(), - "trackIndex": pick_track[0], - # "node": node_serialized - }}) - # for subTrackItem on track without any trackItems - elif len(items_check) == 0: - effects.update({et_item.name(): { - "timelineIn": effect_t_in, - "timelineOut": effect_t_out, - "subTrackIndex": et_item.subTrackIndex(), - "trackIndex": pick_track[0], - "node": node_serialized - }}) + # pick track index from subTrackItem + pick_sub_track = [indx for indx, vt + in enumerate(sequence.videoTracks()) + if vt.name() in sub_track] + # pick track index from trackItem + pick_track = [indx for indx, vt in enumerate(sequence.videoTracks()) + if vt.name() in track] + # collect timelineIn/Out + effect_t_in = int(et_item.timelineIn()) + effect_t_out = int(et_item.timelineOut()) + + # controle if parent track has video trackItems + items_check = et_item.parent().items() + + # filter out all track items under any track with effects + # also filter out track item bellow + if (pick_track[0] in effects_on_tracks) and (pick_sub_track[0] >= pick_track[0]): + if (effect_t_in == timeline_in) and (effect_t_out == timeline_out): + effects["effects"].update({et_item.name(): { + "timelineIn": effect_t_in, + "timelineOut": effect_t_out, + "subTrackIndex": et_item.subTrackIndex(), + "trackIndex": pick_track[0], + "node": node_serialized + }}) + # for subTrackItem on track without any trackItems + elif len(items_check) == 0: + effects["effects"].update({et_item.name(): { + "timelineIn": effect_t_in, + "timelineOut": effect_t_out, + "subTrackIndex": et_item.subTrackIndex(), + "trackIndex": pick_track[0], + "node": node_serialized + }}) instance.data["effectTrackItems"] = effects if len(instance.data.get("effectTrackItems", {}).keys()) > 0: - instance.data["families"] += ["effects"] + instance.data["families"] += ["lut"] self.log.debug("effects.keys: {}".format(instance.data.get("effectTrackItems", {}).keys())) self.log.debug("effects: {}".format(instance.data.get("effectTrackItems", {}))) From d4294eccd983ec1a4a8ba34c96fa128d5f9f61b2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Aug 2019 15:37:15 +0200 Subject: [PATCH 24/26] fix(nks): clean up of the script --- pype/plugins/nukestudio/publish/collect_clips.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index f6b0da1bca..86a28aae48 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -26,10 +26,10 @@ class CollectClips(api.ContextPlugin): # get all subTrackItems and add it to context effects_on_tracks = [] sub_track_items = [] + # looop trough all tracks and search for subtreacks for track_index, video_track in enumerate(sequence.videoTracks()): - sub_items = video_track.subTrackItems() - items = video_track.items() + sub_items = video_track.subTrackItems() if not sub_items: continue for si in sub_items: @@ -43,8 +43,6 @@ class CollectClips(api.ContextPlugin): if (track_index not in effects_on_tracks): effects_on_tracks.append(track_index) - # self.log.debug("_>_ effects_on_tracks `{}`".format(effects_on_tracks)) - # self.log.debug("_|_ sub_track_items `{}`".format(sub_track_items)) # add it to context context.data["subTrackUsedTracks"] = effects_on_tracks context.data["subTrackItems"] = sub_track_items @@ -56,7 +54,7 @@ class CollectClips(api.ContextPlugin): media_type = "core.Hiero.Python.TrackItem.MediaType.kVideo" if str(item.mediaType()) != media_type: continue - except: + except Exception: continue track = item.parent() @@ -93,7 +91,7 @@ class CollectClips(api.ContextPlugin): try: head, padding, ext = os.path.basename(source_path).split(".") source_first_frame = int(padding) - except: + except Exception: source_first_frame = 0 instances_data.append( From 715b282ac92e623e8b024cfb28dae8f648a3d07e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Aug 2019 15:37:40 +0200 Subject: [PATCH 25/26] fix(nks): metadata plugin into `_unused` --- .../nukestudio/_unused/collect_metadata.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 pype/plugins/nukestudio/_unused/collect_metadata.py diff --git a/pype/plugins/nukestudio/_unused/collect_metadata.py b/pype/plugins/nukestudio/_unused/collect_metadata.py new file mode 100644 index 0000000000..23d36ba4a2 --- /dev/null +++ b/pype/plugins/nukestudio/_unused/collect_metadata.py @@ -0,0 +1,30 @@ +from pyblish import api + + +class CollectClipMetadata(api.InstancePlugin): + """Collect Metadata from selected track items.""" + + order = api.CollectorOrder + 0.01 + label = "Collect Metadata" + hosts = ["nukestudio"] + + def process(self, instance): + item = instance.data["item"] + ti_metadata = self.metadata_to_string(dict(item.metadata())) + ms_metadata = self.metadata_to_string( + dict(item.source().mediaSource().metadata())) + + instance.data["clipMetadata"] = ti_metadata + instance.data["mediaSourceMetadata"] = ms_metadata + + self.log.info(instance.data["clipMetadata"]) + self.log.info(instance.data["mediaSourceMetadata"]) + return + + def metadata_to_string(self, metadata): + data = dict() + for k, v in metadata.items(): + if v not in ["-", ""]: + data[str(k)] = v + + return data From bbee1230d5201588f92129add5c49c8642eaa21c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 19 Aug 2019 15:38:02 +0200 Subject: [PATCH 26/26] feat(nks): adding family `lut` --- pype/plugins/global/publish/integrate_new.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index c416cf3fc7..e5d8007d70 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -63,6 +63,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "rig", "plate", "look", + "lut", "audio" ] exclude_families = ["clip"]