From 14520c4a479b60da2cce714c6ac4d91d3e59760d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Nov 2019 23:16:22 +0100 Subject: [PATCH 1/7] feat(nks): refactoring workfiles - adding `event.py` and moving to event based system - fixing registred families - fixing opening workfile and closing `untitled` project - fixing default tags creation only to project --- pype/nukestudio/__init__.py | 55 +++--------------- pype/nukestudio/events.py | 107 ++++++++++++++++++++++++++++++++++++ pype/nukestudio/lib.py | 30 ++++++++-- pype/nukestudio/tags.py | 10 +++- pype/nukestudio/workio.py | 48 ++++++++++++++-- 5 files changed, 190 insertions(+), 60 deletions(-) create mode 100644 pype/nukestudio/events.py diff --git a/pype/nukestudio/__init__.py b/pype/nukestudio/__init__.py index 10053eb8bb..097f077e15 100644 --- a/pype/nukestudio/__init__.py +++ b/pype/nukestudio/__init__.py @@ -1,6 +1,5 @@ import os from pypeapp import Logger -import hiero from avalon import api as avalon from pyblish import api as pyblish @@ -17,7 +16,8 @@ from .menu import ( install as menu_install, _update_menu_task_label ) -from .tags import add_tags_from_presets + +from .events import register_hiero_events __all__ = [ # Workfiles API @@ -56,7 +56,8 @@ def install(config): Installing Nukestudio integration for avalon Args: - config (obj): avalon config module `pype` in our case, it is not used but required by avalon.api.install() + config (obj): avalon config module `pype` in our case, it is not + used but required by avalon.api.install() """ @@ -73,7 +74,8 @@ def install(config): # Disable all families except for the ones we explicitly want to see family_states = [ "write", - "review" + "review", + "plate" ] avalon.data["familiesStateDefault"] = False @@ -82,49 +84,8 @@ def install(config): # install menu menu_install() - # Workfiles. - launch_workfiles = os.environ.get("WORKFILES_STARTUP") - - if launch_workfiles: - hiero.core.events.registerInterest( - "kAfterNewProjectCreated", launch_workfiles_app - ) - - # Add tags on project load. - hiero.core.events.registerInterest( - "kAfterProjectLoad", add_tags - ) - - -def add_tags(event): - """ - Event for automatic tag creation after nukestudio start - - Args: - event (obj): required but unused - """ - - add_tags_from_presets() - - -def launch_workfiles_app(event): - """ - Event for launching workfiles after nukestudio start - - Args: - event (obj): required but unused - """ - from .lib import set_workfiles - - set_workfiles() - - # Closing the new project. - event.sender.close() - - # Deregister interest as its a one-time launch. - hiero.core.events.unregisterInterest( - "kAfterNewProjectCreated", launch_workfiles_app - ) + # register hiero events + register_hiero_events() def uninstall(): diff --git a/pype/nukestudio/events.py b/pype/nukestudio/events.py new file mode 100644 index 0000000000..822dc4db87 --- /dev/null +++ b/pype/nukestudio/events.py @@ -0,0 +1,107 @@ +import os +import hiero.core.events +from pypeapp import Logger +from .lib import sync_avalon_data_to_workfile, launch_workfiles_app +from .tags import add_tags_from_presets + +log = Logger().get_logger(__name__, "nukestudio") + + +def startupCompleted(event): + log.info("startup competed event...") + return + + +def shutDown(event): + log.info("shut down event...") + return + + +def beforeNewProjectCreated(event): + log.info("before new project created event...") + return + + +def afterNewProjectCreated(event): + log.info("after new project created event...") + # sync avalon data to project properities + sync_avalon_data_to_workfile() + + # add tags from preset + add_tags_from_presets() + + # Workfiles. + if int(os.environ.get("WORKFILES_STARTUP", "0")): + hiero.core.events.sendEvent("kStartWorkfiles", None) + # reset workfiles startup not to open any more in session + os.environ["WORKFILES_STARTUP"] = "0" + + +def beforeProjectLoad(event): + log.info("before project load event...") + return + + +def afterProjectLoad(event): + log.info("after project load event...") + # sync avalon data to project properities + sync_avalon_data_to_workfile() + + # add tags from preset + add_tags_from_presets() + + +def beforeProjectClosed(event): + log.info("before project closed event...") + return + + +def afterProjectClosed(event): + log.info("after project closed event...") + return + + +def beforeProjectSaved(event): + log.info("before project saved event...") + return + + +def afterProjectSaved(event): + log.info("after project saved event...") + return + + +def register_hiero_events(): + log.info( + "Registering events for: kBeforeNewProjectCreated, " + "kAfterNewProjectCreated, kBeforeProjectLoad, kAfterProjectLoad, " + "kBeforeProjectSave, kAfterProjectSave, kBeforeProjectClose, " + "kAfterProjectClose, kShutdown, kStartup" + ) + + # hiero.core.events.registerInterest( + # "kBeforeNewProjectCreated", beforeNewProjectCreated) + hiero.core.events.registerInterest( + "kAfterNewProjectCreated", afterNewProjectCreated) + + # hiero.core.events.registerInterest( + # "kBeforeProjectLoad", beforeProjectLoad) + hiero.core.events.registerInterest( + "kAfterProjectLoad", afterProjectLoad) + + # hiero.core.events.registerInterest( + # "kBeforeProjectSave", beforeProjectSaved) + # hiero.core.events.registerInterest( + # "kAfterProjectSave", afterProjectSaved) + # + # hiero.core.events.registerInterest( + # "kBeforeProjectClose", beforeProjectClosed) + # hiero.core.events.registerInterest( + # "kAfterProjectClose", afterProjectClosed) + # + # hiero.core.events.registerInterest("kShutdown", shutDown) + # hiero.core.events.registerInterest("kStartup", startupCompleted) + + # workfiles + hiero.core.events.registerEventType("kStartWorkfiles") + hiero.core.events.registerInterest("kStartWorkfiles", launch_workfiles_app) diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index 81b48f294d..c71e2cb999 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -25,19 +25,26 @@ def set_workfiles(): ''' Wrapping function for workfiles launcher ''' from avalon.tools import workfiles - # import session to get project dir - S = avalon.Session - active_project_root = os.path.normpath( - os.path.join(S['AVALON_PROJECTS'], S['AVALON_PROJECT']) - ) workdir = os.environ["AVALON_WORKDIR"] # show workfile gui workfiles.show(workdir) +def sync_avalon_data_to_workfile(): + # import session to get project dir + S = avalon.Session + active_project_root = os.path.normpath( + os.path.join(S['AVALON_PROJECTS'], S['AVALON_PROJECT']) + ) # getting project project = hiero.core.projects()[-1] + if "Tag Presets" in project.name(): + return + + log.debug("Synchronizing Pype metadata to project: {}".format( + project.name())) + # set project root with backward compatibility try: project.setProjectDirectory(active_project_root) @@ -48,7 +55,7 @@ def set_workfiles(): # get project data from avalon db project_data = pype.get_project()["data"] - log.info("project_data: {}".format(project_data)) + log.debug("project_data: {}".format(project_data)) # get format and fps property from avalon db on project width = project_data["resolutionWidth"] @@ -68,6 +75,17 @@ def set_workfiles(): log.info("Project property has been synchronised with Avalon db") +def launch_workfiles_app(event): + """ + Event for launching workfiles after nukestudio start + + Args: + event (obj): required but unused + """ + set_workfiles() + + + def reload_config(): """Attempt to reload pipeline at run-time. diff --git a/pype/nukestudio/tags.py b/pype/nukestudio/tags.py index 8ae88d731c..3a15f9f39e 100644 --- a/pype/nukestudio/tags.py +++ b/pype/nukestudio/tags.py @@ -52,7 +52,13 @@ def add_tags_from_presets(): """ Will create default tags from presets. """ + project = hiero.core.projects()[-1] + if "Tag Presets" in project.name(): + return + + log.debug("Setting default tags on project: {}".format(project.name())) + # get all presets presets = config.get_presets() @@ -77,7 +83,7 @@ def add_tags_from_presets(): # Get project assets. Currently Ftrack specific to differentiate between # asset builds and shots. - if int(os.getenv("TAG_ASSETBUILD_STARTUP", 0)) is 1: + if int(os.getenv("TAG_ASSETBUILD_STARTUP", 0)) == 1: nks_pres_tags["[AssetBuilds]"] = {} for asset in io.find({"type": "asset"}): if asset["data"]["entityType"] == "AssetBuild": @@ -150,3 +156,5 @@ def add_tags_from_presets(): # update only non hierarchy tags # because hierarchy could be edited update_tag(_t, _val) + + log.info("Default Tags were set...") diff --git a/pype/nukestudio/workio.py b/pype/nukestudio/workio.py index 3b6b3a56c9..1681d8a2ab 100644 --- a/pype/nukestudio/workio.py +++ b/pype/nukestudio/workio.py @@ -1,10 +1,11 @@ import os - import hiero - from avalon import api +from pypeapp import Logger +log = Logger().get_logger(__name__, "nukestudio") + def file_extensions(): return [".hrox"] @@ -12,20 +13,55 @@ def file_extensions(): def has_unsaved_changes(): # There are no methods for querying unsaved changes to a project, so # enforcing to always save. - return True + # but we could at least check if a current open script has a path + project = hiero.core.projects()[-1] + if project.path(): + return True + else: + return False def save_file(filepath): project = hiero.core.projects()[-1] - if project: + + # close `Untitled` project + if "Untitled" not in project.name(): + log.info("Saving project: `{}`".format(project.name())) project.saveAs(filepath) - else: + elif not project: + log.info("Creating new project...") project = hiero.core.newProject() project.saveAs(filepath) + else: + log.info("Dropping `Untitled` project...") + return def open_file(filepath): - hiero.core.openProject(filepath) + """Manually fire the kBeforeProjectLoad event in order to work around a bug in Hiero. + The Foundry has logged this bug as: + Bug 40413 - Python API - kBeforeProjectLoad event type is not triggered + when calling hiero.core.openProject() (only triggered through UI) + It exists in all versions of Hiero through (at least) v1.9v1b12. + + Once this bug is fixed, a version check will need to be added here in order to + prevent accidentally firing this event twice. The following commented-out code + is just an example, and will need to be updated when the bug is fixed to catch the + correct versions.""" + # if (hiero.core.env['VersionMajor'] < 1 or + # hiero.core.env['VersionMajor'] == 1 and hiero.core.env['VersionMinor'] < 10: + hiero.core.events.sendEvent("kBeforeProjectLoad", None) + + project = hiero.core.projects()[-1] + + # open project file + hiero.core.openProject(filepath.replace(os.path.sep, "/")) + + # close previous project + project.close() + + + return True From 5eb22c99c899bde775fe9a97d737d895ef9c1b85 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 28 Nov 2019 16:59:29 +0100 Subject: [PATCH 2/7] bugfix: remove ffmpeg from maya review extractions --- pype/plugins/maya/publish/extract_quicktime.py | 13 +++++-------- pype/plugins/maya/publish/extract_thumbnail.py | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/pype/plugins/maya/publish/extract_quicktime.py b/pype/plugins/maya/publish/extract_quicktime.py index 975ab138fa..1031955260 100644 --- a/pype/plugins/maya/publish/extract_quicktime.py +++ b/pype/plugins/maya/publish/extract_quicktime.py @@ -1,19 +1,16 @@ import os -import subprocess import contextlib -import json import capture_gui import clique - +# import pype.maya.lib as lib import pype.api -import avalon.maya - +# from maya import cmds, mel import pymel.core as pm -import ffmpeg -# from pype.scripts import otio_burnin -reload(ffmpeg) +# import ffmpeg +# # from pype.scripts import otio_burnin +# reload(ffmpeg) # TODO: move codec settings to presets diff --git a/pype/plugins/maya/publish/extract_thumbnail.py b/pype/plugins/maya/publish/extract_thumbnail.py index 8751509cd2..dc8044cf19 100644 --- a/pype/plugins/maya/publish/extract_thumbnail.py +++ b/pype/plugins/maya/publish/extract_thumbnail.py @@ -11,8 +11,8 @@ import pype.api from maya import cmds import pymel.core as pm -import ffmpeg -reload(ffmpeg) +# import ffmpeg +# reload(ffmpeg) import avalon.maya From e1f1eb6e0dfa8e89a9574045fb78cace5ddcca65 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 29 Nov 2019 01:00:17 +0100 Subject: [PATCH 3/7] fix(global): `source` family in wrong plugin --- pype/plugins/global/publish/integrate_new.py | 3 ++- pype/plugins/global/publish/integrate_rendered_frames.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 4a6edf7442..ab3da29cac 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -65,7 +65,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "lut", "audio", "yetiRig", - "yeticache" + "yeticache", + "source" ] exclude_families = ["clip"] diff --git a/pype/plugins/global/publish/integrate_rendered_frames.py b/pype/plugins/global/publish/integrate_rendered_frames.py index 945539462e..086b03802e 100644 --- a/pype/plugins/global/publish/integrate_rendered_frames.py +++ b/pype/plugins/global/publish/integrate_rendered_frames.py @@ -24,7 +24,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin): label = "Integrate Frames" order = pyblish.api.IntegratorOrder - families = ["imagesequence", "source"] + families = ["imagesequence"] family_targets = [".frames", ".local", ".review", "imagesequence", "render", "source"] exclude_families = ["clip"] From 66808d34fe7871f9a8cb0ea459a955191d618fff Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 29 Nov 2019 01:01:44 +0100 Subject: [PATCH 4/7] fix(nuke): create write should return created write --- pype/plugins/nuke/create/create_write.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/plugins/nuke/create/create_write.py b/pype/plugins/nuke/create/create_write.py index 1634af22ab..cc66274626 100644 --- a/pype/plugins/nuke/create/create_write.py +++ b/pype/plugins/nuke/create/create_write.py @@ -40,7 +40,6 @@ class CreateWriteRender(plugin.PypeCreator): def process(self): from pype.nuke import lib as pnlib - reload(pnlib) inputs = [] outputs = [] @@ -101,7 +100,7 @@ class CreateWriteRender(plugin.PypeCreator): for output in outputs: output.setInput(0, write_node) - return True + return write_node # # class CreateWritePrerender(avalon.nuke.Creator): From 9b305c7e1167c46fb61593c55eb00a58ce07a223 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 29 Nov 2019 01:02:07 +0100 Subject: [PATCH 5/7] fix(nuke): create read was not updated to new scheme --- pype/plugins/nuke/create/create_read.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pype/plugins/nuke/create/create_read.py b/pype/plugins/nuke/create/create_read.py index e033bc63b0..87bb45a6ad 100644 --- a/pype/plugins/nuke/create/create_read.py +++ b/pype/plugins/nuke/create/create_read.py @@ -17,21 +17,24 @@ class CrateRead(avalon.nuke.Creator): family = "source" families = family icon = "film" + defaults = ["Effect", "Backplate", "Fire", "Smoke"] def __init__(self, *args, **kwargs): super(CrateRead, self).__init__(*args, **kwargs) - + self.nodes = nuke.selectedNodes() data = OrderedDict() data['family'] = self.family data['families'] = self.families - {data.update({k: v}) for k, v in self.data.items() - if k not in data.keys()} + + for k, v in self.data.items(): + if k not in data.keys(): + data.update({k: v}) + self.data = data def process(self): self.name = self.data["subset"] - - nodes = nuke.selectedNodes() + nodes = self.nodes if not nodes or len(nodes) == 0: nuke.message('Please select Read node') @@ -40,9 +43,8 @@ class CrateRead(avalon.nuke.Creator): for node in nodes: if node.Class() != 'Read': continue - name = node["name"].value() avalon_data = self.data - avalon_data['subset'] = "{}_{}".format(self.family, name) + avalon_data['subset'] = "{}".format(self.name) self.change_read_node(self.data["subset"], node, avalon_data) count_reads += 1 @@ -52,4 +54,4 @@ class CrateRead(avalon.nuke.Creator): def change_read_node(self, name, node, data): node = avalon.nuke.lib.imprint(node, data) - node['tile_color'].setValue(16711935) + node['tile_color'].setValue(16744935) From 8cb0db829a294df4abfdf5c640a21bbccd969d79 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 29 Nov 2019 01:02:37 +0100 Subject: [PATCH 6/7] fix(nuke): collect instance was not expecting Read node --- .../plugins/nuke/publish/collect_instances.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index 7f2e1566c3..5303dc6456 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -15,16 +15,17 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): hosts = ["nuke", "nukeassist"] def process(self, context): + asset_data = io.find_one({"type": "asset", "name": api.Session["AVALON_ASSET"]}) - self.log.debug("asset_data: {}".format(asset_data["data"])) instances = [] # creating instances per write node self.log.debug("nuke.allNodes(): {}".format(nuke.allNodes())) for node in nuke.allNodes(): + try: if node["disable"].value(): continue @@ -59,14 +60,16 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): node.end() family = avalon_knob_data["families"] - if node["render"].value(): - self.log.info("flagged for render") - family = "render.local" - # dealing with local/farm rendering - if node["render_farm"].value(): - self.log.info("adding render farm family") - family = "render.farm" - instance.data['transfer'] = False + + if node.Class() not in "Read": + if node["render"].value(): + self.log.info("flagged for render") + family = "render.local" + # dealing with local/farm rendering + if node["render_farm"].value(): + self.log.info("adding render farm family") + family = "render.farm" + instance.data['transfer'] = False instance.data.update({ "subset": subset, From 9044056aef4663977fce67f08b935aa91973547f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 29 Nov 2019 01:02:56 +0100 Subject: [PATCH 7/7] fix(nuke): collect read fixed to new scheme --- pype/plugins/nuke/publish/collect_reads.py | 178 ++++++++++++--------- 1 file changed, 99 insertions(+), 79 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_reads.py b/pype/plugins/nuke/publish/collect_reads.py index 352ae129f4..7fda6334bb 100644 --- a/pype/plugins/nuke/publish/collect_reads.py +++ b/pype/plugins/nuke/publish/collect_reads.py @@ -1,112 +1,132 @@ import os import re -import clique import nuke import pyblish.api -import logging from avalon import io, api -log = logging.get_logger(__name__) - @pyblish.api.log -class CollectNukeReads(pyblish.api.ContextPlugin): +class CollectNukeReads(pyblish.api.InstancePlugin): """Collect all read nodes.""" - order = pyblish.api.CollectorOrder + 0.1 + order = pyblish.api.CollectorOrder + 0.04 label = "Collect Reads" - hosts = ["nuke"] + hosts = ["nuke", "nukeassist"] + families = ["source"] - def process(self, context): + def process(self, instance): asset_data = io.find_one({"type": "asset", "name": api.Session["AVALON_ASSET"]}) + self.log.debug("asset_data: {}".format(asset_data["data"])) - for instance in context.data["instances"]: - self.log.debug("checking instance: {}".format(instance)) - node = instance[0] - if node.Class() != "Read": - continue + self.log.debug("checking instance: {}".format(instance)) - file_path = node["file"].value() - file_name = os.path.basename(file_path) - items = file_name.split(".") + node = instance[0] + if node.Class() != "Read": + return - if len(items) < 2: - raise ValueError + file_path = node["file"].value() + file_name = os.path.basename(file_path) + items = file_name.split(".") - ext = items[-1] + if len(items) < 2: + raise ValueError - # # Get frame range - first_frame = node['first'].value() - last_frame = node['last'].value() + ext = items[-1] - # # Easier way to sequence - Not tested - # isSequence = True - # if first_frame == last_frame: - # isSequence = False + # Get frame range + handle_start = instance.context.data["handleStart"] + handle_end = instance.context.data["handleEnd"] + first_frame = node['first'].value() + last_frame = node['last'].value() - isSequence = False - if len(items) > 1: - sequence = items[-2] - hash_regex = re.compile(r'([#*])') - seq_regex = re.compile('[%0-9*d]') - hash_match = re.match(hash_regex, sequence) - seq_match = re.match(seq_regex, sequence) - if hash_match or seq_match: - isSequence = True + # colorspace + colorspace = node["colorspace"].value() + if "default" in colorspace: + colorspace = colorspace.replace("default (", "").replace(")", "") - # get source path - path = nuke.filename(node) - source_dir = os.path.dirname(path) - self.log.debug('source dir: {}'.format(source_dir)) + # # Easier way to sequence - Not tested + # isSequence = True + # if first_frame == last_frame: + # isSequence = False - if isSequence: - source_files = os.listdir(source_dir) - else: - source_files = file_name + isSequence = False + if len(items) > 1: + sequence = items[-2] + hash_regex = re.compile(r'([#*])') + seq_regex = re.compile(r'[%0-9*d]') + hash_match = re.match(hash_regex, sequence) + seq_match = re.match(seq_regex, sequence) + if hash_match or seq_match: + isSequence = True - # Include start and end render frame in label - name = node.name() - label = "{0} ({1}-{2})".format( - name, - int(first_frame), - int(last_frame) - ) + # get source path + path = nuke.filename(node) + source_dir = os.path.dirname(path) + self.log.debug('source dir: {}'.format(source_dir)) - self.log.debug("collected_frames: {}".format(label)) + if isSequence: + source_files = [f for f in os.listdir(source_dir) + if ext in f + if items[0] in f] + else: + source_files = file_name - if "representations" not in instance.data: - instance.data["representations"] = [] + # Include start and end render frame in label + name = node.name() + label = "{0} ({1}-{2})".format( + name, + int(first_frame), + int(last_frame) + ) - representation = { - 'name': ext, - 'ext': "." + ext, - 'files': source_files, - "stagingDir": source_dir, - } - instance.data["representations"].append(representation) + self.log.debug("collected_frames: {}".format(label)) - transfer = False - if "publish" in node.knobs(): - transfer = node["publish"] + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data['transfer'] = transfer + representation = { + 'name': ext, + 'ext': ext, + 'files': source_files, + "stagingDir": source_dir, + "frameStart": "%0{}d".format( + len(str(last_frame))) % first_frame + } + instance.data["representations"].append(representation) - self.log.debug("checking for error: {}".format(label)) - instance.data.update({ - "path": path, - "stagingDir": source_dir, - "ext": ext, - "label": label, - "frameStart": first_frame, - "frameEnd": last_frame, - "colorspace": node["colorspace"].value(), - "handles": int(asset_data["data"].get("handles", 0)), - "step": 1, - "fps": int(nuke.root()['fps'].value()) - }) + transfer = False + if "publish" in node.knobs(): + transfer = node["publish"] - self.log.debug("instance.data: {}".format(instance.data)) + instance.data['transfer'] = transfer - self.log.debug("context: {}".format(context)) + # Add version data to instance + version_data = { + "handles": handle_start, + "handleStart": handle_start, + "handleEnd": handle_end, + "frameStart": first_frame + handle_start, + "frameEnd": last_frame - handle_end, + "colorspace": colorspace, + "families": [instance.data["family"]], + "subset": instance.data["subset"], + "fps": instance.context.data["fps"] + } + + instance.data.update({ + "versionData": version_data, + "path": path, + "stagingDir": source_dir, + "ext": ext, + "label": label, + "frameStart": first_frame, + "frameEnd": last_frame, + "colorspace": colorspace, + "handles": int(asset_data["data"].get("handles", 0)), + "step": 1, + "fps": int(nuke.root()['fps'].value()) + }) + + self.log.debug("instance.data: {}".format(instance.data))