def _buildAdditionalNodes(self, item): # Callback from script generation to add additional nodes nodes = [] data = self._preset.properties()["additionalNodesData"] itemType = None if isinstance(item, hiero.core.Clip): itemType = FnExternalRender.kPerShot elif isinstance(item, hiero.core.TrackItem): itemType = FnExternalRender.kPerShot elif isinstance(item, (hiero.core.VideoTrack, hiero.core.AudioTrack)): itemType = FnExternalRender.kPerTrack elif isinstance(item, hiero.core.Sequence): itemType = FnExternalRender.kPerSequence if itemType: if self._preset.properties()["additionalNodesEnabled"]: nodes.extend( FnExternalRender.createAdditionalNodes( itemType, data, item)) if self._preset.properties()["useAssets"]: # Allow registered listeners to work with the script manager = FnAssetAPI.Events.getEventManager() ## @todo Assuming that we're always on a thread for now, this should be verified manager.blockingEvent(False, 'hieroToNukeScriptAddNodesForItem', itemType, item, nodes) return nodes
def _add_default_presets(self, overwrite): """ Hiero std method to add new exporter presets. Passed in to hiero.core.taskRegistry.setDefaultPresets() as a function pointer. """ # add all built-in defaults self._old_AddDefaultPresets_fn(overwrite) # Add Shotgun template name = "Basic Shotgun Shot" localpresets = [preset.name() for preset in hiero.core.taskRegistry.localPresets()] # only add the preset if it is not already there - or if a reset to defaults is requested. if overwrite or name not in localpresets: # grab all our path templates plate_template = self.get_template("template_plate_path") script_template = self.get_template("template_nuke_script_path") render_template = self.get_template("template_render_path") # call the hook to translate them into hiero paths, using hiero keywords plate_hiero_str = self.execute_hook("hook_translate_template", template=plate_template, output_type='plate') self.log_debug("Translated %s --> %s" % (plate_template, plate_hiero_str)) script_hiero_str = self.execute_hook("hook_translate_template", template=script_template, output_type='script') self.log_debug("Translated %s --> %s" % (script_template, script_hiero_str)) render_hiero_str = self.execute_hook("hook_translate_template", template=render_template, output_type='render') self.log_debug("Translated %s --> %s" % (render_template, render_hiero_str)) # check so that no unknown keywords exist in the templates after translation self._validate_hiero_export_template(plate_hiero_str) self._validate_hiero_export_template(script_hiero_str) self._validate_hiero_export_template(render_hiero_str) # and set the default properties to be based off of those templates # Set the quicktime defaults per our hook file_type, file_options = self.execute_hook("hook_get_quicktime_settings", for_shotgun=False) properties = { "exportTemplate": ( (script_hiero_str, ShotgunNukeShotPreset("", {"readPaths": [], "writePaths": []})), (render_hiero_str, FnExternalRender.NukeRenderPreset("", {"file_type": "dpx", "dpx": {"datatype": "10 bit"}})), (plate_hiero_str, ShotgunTranscodePreset("", {"file_type": file_type, file_type: file_options})), ) } preset = ShotgunShotProcessorPreset(name, properties) hiero.core.taskRegistry.removeProcessorPreset(name) hiero.core.taskRegistry.addProcessorPreset(name, preset)
def buildScript(self): """ Override the default buildScript functionality to also output a temp movie file if needed for uploading to Shotgun """ # Build the usual script FnTranscodeExporter.TranscodeExporter.buildScript(self) # If we are not creating a version then we do not need the extra node if not self._preset.properties()['create_version']: return if self._preset.properties()['file_type'] in ["mov", "ffmpeg"]: # already outputting a mov file, use that for upload self._quicktime_path = self.resolvedExportPath() self._temp_quicktime = False return self._quicktime_path = os.path.join(tempfile.mkdtemp(), 'preview.mov') self._temp_quicktime = True nodeName = "Shotgun Screening Room Media" framerate = None if self._sequence: framerate = self._sequence.framerate() if self._clip.framerate().isValid(): framerate = self._clip.framerate() preset = FnTranscodeExporter.TranscodePreset("Qt Write", self._preset.properties()) # insert the write node to generate the quicktime file_type, properties = self.app.execute_hook( "hook_get_quicktime_settings", for_shotgun=True) preset.properties().update({ "file_type": file_type, file_type: properties, }) mov_write_node = FnExternalRender.createWriteNode( self._quicktime_path, preset, nodeName, framerate=framerate, projectsettings=self._projectSettings) self._script.addNode(mov_write_node)
def buildScript(self): """ Override the default buildScript functionality to also output a temp movie file if needed for uploading to Shotgun """ # Build the usual script FnTranscodeExporter.TranscodeExporter.buildScript(self) if self._preset.properties()['file_type'] == 'mov': # already outputting a mov file, use that for upload self._quicktime_path = self.resolvedExportPath() self._temp_quicktime = False return self._quicktime_path = os.path.join(tempfile.mkdtemp(), 'preview.mov') self._temp_quicktime = True nodeName = "Shotgun Screening Room Media" framerate = None if self._sequence: framerate = self._sequence.framerate() if self._clip.framerate().isValid(): framerate = self._clip.framerate() preset = FnTranscodeExporter.TranscodePreset("Qt Write", self._preset.properties()) preset.properties().update({ 'file_type': u'mov', 'mov': { 'codec': 'avc1\tH.264', 'quality': 3, 'settingsString': 'H.264, High Quality', 'keyframerate': 1, } }) movWriteNode = FnExternalRender.createWriteNode( self._quicktime_path, preset, nodeName, framerate=framerate, projectsettings=self._projectSettings) self._script.addNode(movWriteNode)
def buildScript(self): """ Override the default buildScript functionality to also output a temp movie file if needed for uploading to Shotgun """ # Build the usual script FnTranscodeExporter.TranscodeExporter.buildScript(self) # If we are not creating a version then we do not need the extra node if not self._preset.properties()['create_version']: return if self._preset.properties()['file_type'] in ["mov", "ffmpeg"]: # already outputting a mov file, use that for upload self._quicktime_path = self.resolvedExportPath() self._temp_quicktime = False return self._quicktime_path = os.path.join(tempfile.mkdtemp(), 'preview.mov') self._temp_quicktime = True nodeName = "Shotgun Screening Room Media" framerate = None if self._sequence: framerate = self._sequence.framerate() if self._clip.framerate().isValid(): framerate = self._clip.framerate() preset = FnTranscodeExporter.TranscodePreset("Qt Write", self._preset.properties()) # insert the write node to generate the quicktime file_type, properties = self.app.execute_hook("hook_get_quicktime_settings", for_shotgun=True) preset.properties().update({ "file_type": file_type, file_type: properties, }) mov_write_node = FnExternalRender.createWriteNode(self._quicktime_path, preset, nodeName, framerate=framerate, projectsettings=self._projectSettings) self._script.addNode(mov_write_node)
def buildScript(self): """ Override the default buildScript functionality to also output a temp movie file if needed for uploading to Shotgun """ # Build the usual script FnTranscodeExporter.TranscodeExporter.buildScript(self) if self._preset.properties()['file_type'] == 'mov': # already outputting a mov file, use that for upload self._quicktime_path = self.resolvedExportPath() self._temp_quicktime = False return self._quicktime_path = os.path.join(tempfile.mkdtemp(), 'preview.mov') self._temp_quicktime = True nodeName = "Shotgun Screening Room Media" framerate = None if self._sequence: framerate = self._sequence.framerate() if self._clip.framerate().isValid(): framerate = self._clip.framerate() preset = FnTranscodeExporter.TranscodePreset("Qt Write", self._preset.properties()) preset.properties().update({ 'file_type': u'mov', 'mov': { 'codec': 'avc1\tH.264', 'quality': 3, 'settingsString': 'H.264, High Quality', 'keyframerate': 1, } }) movWriteNode = FnExternalRender.createWriteNode(self._quicktime_path, preset, nodeName, framerate=framerate, projectsettings=self._projectSettings) self._script.addNode(movWriteNode)
def writeSequenceToScript(self, script): # Get the range to set on the Root node. This is the range the nuke script will render by default. start, end = self.outputRange() log.debug("TranscodeExporter: rootNode range is %s %s", start, end) framerate = self._sequence.framerate() dropFrames = self._sequence.dropFrame() fps = framerate.toFloat() rootNode = self.makeRootNode(start, end, fps) script.addNode(rootNode) # Add Unconnected additional nodes if self._preset.properties()["additionalNodesEnabled"]: script.addNode( FnExternalRender.createAdditionalNodes( FnExternalRender.kUnconnected, self._preset.properties()["additionalNodesData"], self._item)) # Force track items to be reformatted to fit the sequence in this case reformatMethod = { "to_type": nuke.ReformatNode.kCompReformatToSequence, "filter": self._preset.properties()["reformat"].get("filter") } if self._preset.properties()["add_image"]: watermark_image = get_watermark_read( self._preset.properties()["watermark_path"], self._preset.properties()["watermark_premult"]) premult_node = nuke.Node('Premult') watermark_transform = nuke.Node('Transform') watermark_transform.setKnob( 'translate', self._preset.properties()["watermark_transform"]) script.addNode(watermark_image) script.addNode(premult_node) script.addNode(watermark_transform) # Build out the sequence. scriptParams = FnNukeHelpersV2.ScriptWriteParameters( includeAnnotations=False, includeEffects=self.includeEffects(), retimeMethod=self._preset.properties()["method"], reformatMethod=reformatMethod, additionalNodesCallback=self._buildAdditionalNodes, views=self.views()) script.pushLayoutContext("sequence", self._sequence.name(), disconnected=False) sequenceWriter = FnNukeHelpersV2.SequenceScriptWriter( self._sequence, scriptParams) sequenceWriter.writeToScript(script, offset=0, skipOffline=self._skipOffline, mediaToSkip=self._mediaToSkip, disconnected=False, masterTracks=None) script.popLayoutContext() script.pushLayoutContext("write", "%s_Render" % self._item.name()) # Create metadata node metadataNode = nuke.MetadataNode(metadatavalues=[( "hiero/project", self._projectName), ("hiero/project_guid", self._project.guid())]) # Add sequence Tags to metadata metadataNode.addMetadataFromTags(self._sequence.tags()) # Apply timeline offset to nuke output script.addNode( nuke.AddTimeCodeNode( timecodeStart=self._sequence.timecodeStart(), fps=framerate, dropFrames=dropFrames, frame=0 if self._startFrame is None else self._startFrame)) # The AddTimeCode field will insert an integer framerate into the metadata, if the framerate is floating point, we need to correct this metadataNode.addMetadata([("input/frame_rate", framerate.toFloat())]) # And next the Write. script.addNode(metadataNode) # Add Burnin group (if enabled) self.addBurninNodes(script) if self._preset.properties()["add_image"]: merge_node = nuke.MergeNode() script.addNode(merge_node) # Get the output format, either from the sequence or the preset, and set it as the root format. # If a reformat is specified in the preset, add it immediately before the Write node. outputReformatNode = self._sequence.format().addToNukeScript( resize=nuke.ReformatNode.kResizeNone, black_outside=False) self._addReformatNode(script, rootNode, outputReformatNode) self.addWriteNodeToScript(script, rootNode, framerate) script.addNode(createViewerNode(self._projectSettings)) script.popLayoutContext()
def writeClipOrTrackItemToScript(self, script): isMovieContainerFormat = self._preset.properties()["file_type"] in ( "mov", "mov32", "mov64", "ffmpeg") start, end = self.outputRange(ignoreRetimes=True, clampToSource=False) unclampedStart = start log.debug("rootNode range is %s %s", start, end) firstFrame, lastFrame = start, end if self._startFrame is not None: firstFrame = self._startFrame # if startFrame is negative we can only assume this is intentional if start < 0 and (self._startFrame is None or self._startFrame >= 0 ) and not isMovieContainerFormat: # We dont want to export an image sequence with a negative frame numbers self.setWarning( "%i Frames of handles will result in a negative frame index.\nFirst frame clamped to 0." % self._cutHandles) start = 0 # The clip framerate may be invalid, if so, use parent sequence framerate fps, framerate, dropFrames = None, None, False if self._sequence: framerate = self._sequence.framerate() dropFrames = self._sequence.dropFrame() if self._clip.framerate().isValid(): framerate = self._clip.framerate() dropFrames = self._clip.dropFrame() if framerate: fps = framerate.toFloat() # Create root node, this defines global frame range and framerate rootNode = self.makeRootNode(start, end, fps) script.addNode(rootNode) if self._preset.properties()["add_image"]: watermark_image = get_watermark_read( self._preset.properties()["watermark_path"], self._preset.properties()["watermark_premult"]) premult_node = nuke.Node('Premult') watermark_transform = nuke.Node('Transform') watermark_transform.setKnob( 'translate', self._preset.properties()["watermark_transform"]) script.addNode(watermark_image) script.addNode(premult_node) script.addNode(watermark_transform) # Add Unconnected additional nodes if self._preset.properties()["additionalNodesEnabled"]: script.addNode( FnExternalRender.createAdditionalNodes( FnExternalRender.kUnconnected, self._preset.properties()["additionalNodesData"], self._item)) # Now add the Read node. writingClip = isinstance(self._item, hiero.core.Clip) if writingClip: script.pushLayoutContext("clip", self._item.name()) self._clip.addToNukeScript( script, additionalNodesCallback=self._buildAdditionalNodes, firstFrame=firstFrame, trimmed=True, includeEffects=self.includeEffects(), project=self._project ) # _clip has no project set, but one is needed by addToNukeScript to do colorpsace conversions script.popLayoutContext() else: # If there are separate track items for each view, write them out (in reverse # order so the inputs are correct) then add a JoinViews items = self._multiViewTrackItems if self._multiViewTrackItems else [ self._item ] for item in reversed(items): script.pushLayoutContext("clip", item.name()) # Construct a TrackItemExportScriptWriter and write the track item trackItemWriter = TrackItemExportScriptWriter(item) trackItemWriter.setAdditionalNodesCallback( self._buildAdditionalNodes) # Find sequence level effects/annotations which apply to the track item. # Annotations are not currently included by the transcode exporter effects, annotations = FnEffectHelpers.findEffectsAnnotationsForTrackItems( [item]) trackItemWriter.setEffects(self.includeEffects(), effects) # TODO This is being done in both the NukeShotExporter and TranscodeExporter. # There should be fully shared code for doing the handles calculations. fullClipLength = (self._cutHandles is None) if fullClipLength: trackItemWriter.setOutputClipLength() else: trackItemWriter.setOutputHandles(*self.outputHandles()) trackItemWriter.setIncludeRetimes( self._retime, self._preset.properties()["method"]) trackItemWriter.setReformat( self._preset.properties()["reformat"]) trackItemWriter.setFirstFrame(firstFrame) trackItemWriter.writeToScript(script) if self._preset.properties()["add_image"]: merge_node = nuke.MergeNode() script.addNode(merge_node) script.popLayoutContext() if self._multiViewTrackItems: joinViewsNode = nuke.Node("JoinViews", inputs=len(self.views())) script.addNode(joinViewsNode) script.pushLayoutContext("write", "%s_Render" % self._item.name()) metadataNode = nuke.MetadataNode(metadatavalues=[( "hiero/project", self._projectName), ("hiero/project_guid", self._project.guid())]) # Add sequence Tags to metadata metadataNode.addMetadataFromTags(self._clip.tags()) # Need a framerate inorder to create a timecode if framerate: # Apply timeline offset to nuke output if self._cutHandles is None: timeCodeNodeStartFrame = unclampedStart else: startHandle, endHandle = self.outputHandles() timeCodeNodeStartFrame = trackItemTimeCodeNodeStartFrame( unclampedStart, self._item, startHandle, endHandle) script.addNode( nuke.AddTimeCodeNode(timecodeStart=self._clip.timecodeStart(), fps=framerate, dropFrames=dropFrames, frame=timeCodeNodeStartFrame)) # The AddTimeCode field will insert an integer framerate into the metadata, if the framerate is floating point, we need to correct this metadataNode.addMetadata([("input/frame_rate", framerate.toFloat()) ]) script.addNode(metadataNode) # Add Burnin group (if enabled) self.addBurninNodes(script) # Get the output format, either from the clip or the preset, and set it as the root format. # If a reformat is specified in the preset, add it immediately before the Write node. reformatNode = self._clip.format().addToNukeScript(None) self._addReformatNode(script, rootNode, reformatNode) self.addWriteNodeToScript(script, rootNode, framerate) script.addNode(createViewerNode(self._projectSettings)) script.popLayoutContext()
def buildScript(self): """ Override the default buildScript functionality to also output a temp movie file if needed for uploading to Shotgun """ # This is a bit of a hack to account for some changes to the # transcode exporter that ships with Nuke/Hiero 9.0 compared # to earlier versions of Hiero. file_type = self._preset.properties()['file_type'] if file_type in ["mov", "ffmpeg"]: if not self._preset.properties()[file_type].get("encoder"): encoder_name = self.app.get_default_encoder_name() self._preset.properties()[file_type]["encoder"] = encoder_name # Build the usual script FnTranscodeExporter.TranscodeExporter.buildScript(self) # If we are not creating a version then we do not need the extra node if not self._preset.properties()['create_version']: return if file_type in ["mov", "ffmpeg"]: # already outputting a mov file, use that for upload self._quicktime_path = self.resolvedExportPath() self._temp_quicktime = False return self._quicktime_path = os.path.join(tempfile.mkdtemp(), 'preview.mov') self._temp_quicktime = True nodeName = "Shotgun Screening Room Media" framerate = None if self._sequence: framerate = self._sequence.framerate() if self._clip.framerate().isValid(): framerate = self._clip.framerate() preset = FnTranscodeExporter.TranscodePreset("Qt Write", self._preset.properties()) # insert the write node to generate the quicktime file_type, properties = self.app.execute_hook( "hook_get_quicktime_settings", for_shotgun=True) self.app.log_info("Transcode quicktime settings: %s" % (properties, )) preset.properties().update({ "file_type": file_type, file_type: properties, }) # Sadly Foundry has a habit of changing the interfaces of # their Python classes out from under us, so now we're going # to have to handle this the ugly way, via introspecting the # arguments expected by the createWriteNode method. arg_spec = inspect.getargspec(FnExternalRender.createWriteNode) if "projectsettings" in arg_spec.args: kwargs = dict( path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, projectsettings=self._projectSettings, ) elif "ctx" in arg_spec.args: kwargs = dict( ctx=self, path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, project=self._project, ) else: kwargs = dict( path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, project=self._project, ) mov_write_node = FnExternalRender.createWriteNode(**kwargs) self._script.addNode(mov_write_node)
def buildScript(self): """ Override the default buildScript functionality to also output a temp movie file if needed for uploading to Shotgun """ # This is a bit of a hack to account for some changes to the # transcode exporter that ships with Nuke/Hiero 9.0 compared # to earlier versions of Hiero. file_type = self._preset.properties()['file_type'] self.app.log_debug("Transcode export file_type: %s" % file_type) if file_type in ["mov", "ffmpeg"]: if not self._preset.properties()[file_type].get("encoder"): encoder_name = self.app.get_default_encoder_name() self._preset.properties()[file_type]["encoder"] = encoder_name # Build the usual script using the base code FnTranscodeExporter.TranscodeExporter.buildScript(self) self.app.log_debug("Transcode base script built") # If we are not creating a version then we do not need the extra node if not self._preset.properties()['create_version']: return if file_type in ["mov", "ffmpeg"]: # already outputting a mov file, use that for upload self._quicktime_path = self.resolvedExportPath() self._temp_quicktime = False return self._quicktime_path = os.path.join(tempfile.mkdtemp(), 'preview.mov') self._temp_quicktime = True nodeName = "Shotgun Screening Room Media" framerate = None if self._sequence: framerate = self._sequence.framerate() if self._clip.framerate().isValid(): framerate = self._clip.framerate() preset = FnTranscodeExporter.TranscodePreset("Qt Write", self._preset.properties()) # insert the write node to generate the quicktime file_type, properties = self.app.execute_hook( "hook_get_quicktime_settings", for_shotgun=True, base_class=HieroGetQuicktimeSettings, ) self.app.log_info("Transcode quicktime settings: %s" % (properties,)) preset.properties().update({ "file_type": file_type, file_type: properties, }) # Sadly Foundry has a habit of changing the interfaces of # their Python classes out from under us, so now we're going # to have to handle this the ugly way, via introspecting the # arguments expected by the createWriteNode method. arg_spec = inspect.getargspec(FnExternalRender.createWriteNode) if "projectsettings" in arg_spec.args: kwargs = dict( path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, projectsettings=self._projectSettings, ) elif "ctx" in arg_spec.args: kwargs = dict( ctx=self, path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, project=self._project, ) else: kwargs = dict( path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, project=self._project, ) mov_write_node = FnExternalRender.createWriteNode(**kwargs) # We create a push node and connect it to the set node we created just before the base write node was created # This means that our write node will parent to the same node the base write node gets parented to. push_command = nuke.PushNode(self._write_set_node_label) self._script.addNode(push_command) self._script.addNode(mov_write_node)
def buildScript(self): """ Override the default buildScript functionality to also output a temp movie file if needed for uploading to Shotgun """ # This is a bit of a hack to account for some changes to the # transcode exporter that ships with Nuke/Hiero 9.0 compared # to earlier versions of Hiero. file_type = self._preset.properties()['file_type'] if file_type in ["mov", "ffmpeg"]: self._preset.properties()[file_type]["encoder"] = "mov32" # Build the usual script FnTranscodeExporter.TranscodeExporter.buildScript(self) # If we are not creating a version then we do not need the extra node if not self._preset.properties()['create_version']: return if file_type in ["mov", "ffmpeg"]: # already outputting a mov file, use that for upload self._quicktime_path = self.resolvedExportPath() self._temp_quicktime = False return self._quicktime_path = os.path.join(tempfile.mkdtemp(), 'preview.mov') self._temp_quicktime = True #KOJO removing extra write node, using ffmpeg as a post render script nodeName = "Shotgun Screening Room Media" framerate = None if self._sequence: framerate = self._sequence.framerate() if self._clip.framerate().isValid(): framerate = self._clip.framerate() preset = FnTranscodeExporter.TranscodePreset("Qt Write", self._preset.properties()) # insert the write node to generate the quicktime file_type, properties = self.app.execute_hook("hook_get_quicktime_settings", for_shotgun=True) preset.properties().update({ "file_type": file_type, file_type: properties, }) # Sadly Foundry has a habit of changing the interfaces of # their Python classes out from under us, so now we're going # to have to handle this the ugly way, via introspecting the # arguments expected by the createWriteNode method. arg_spec = inspect.getargspec(FnExternalRender.createWriteNode) if "projectsettings" in arg_spec.args: kwargs = dict( path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, projectsettings=self._projectSettings, ) elif "ctx" in arg_spec.args: kwargs = dict( ctx=self, path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, project=self._project, ) else: kwargs = dict( path=self._quicktime_path, preset=preset, nodeName=nodeName, framerate=framerate, project=self._project, ) mov_write_node = FnExternalRender.createWriteNode(**kwargs) #self._script.addNode(mov_write_node) #Kojo: using ffmpeg as a post render script self.app.log_debug(dir(mov_write_node)) nodes = self._script.getNodes() for x in nodes: if x.type() == 'Write': x.setKnob('afterRender',"sendToFFmpeg('%s')" %(self._quicktime_path.replace('\\','/')))
def taskStep(self): FnShotExporter.ShotTask.taskStep(self) if self._nothingToDo: return False eventManager = FnAssetAPI.Events.getEventManager() script = nuke.ScriptWriter() start, end = self.outputRange(ignoreRetimes=True, clampToSource=False) unclampedStart = start hiero.core.log.debug("rootNode range is %s %s %s", start, end, self._startFrame) firstFrame = start if self._startFrame is not None: firstFrame = self._startFrame # if startFrame is negative we can only assume this is intentional if start < 0 and (self._startFrame is None or self._startFrame >= 0): # We dont want to export an image sequence with negative frame numbers self.setWarning( "%i Frames of handles will result in a negative frame index.\nFirst frame clamped to 0." % self._cutHandles) start = 0 # Clip framerate may be invalid, then use parent sequence framerate framerate = self._sequence.framerate() dropFrames = self._sequence.dropFrame() if self._clip and self._clip.framerate().isValid(): framerate = self._clip.framerate() dropFrames = self._clip.dropFrame() fps = framerate.toFloat() # Create the root node, this specifies the global frame range and frame rate rootNode = nuke.RootNode(start, end, fps) rootNode.addProjectSettings(self._projectSettings) #rootNode.setKnob("project_directory", os.path.split(self.resolvedExportPath())[0]) script.addNode(rootNode) reformat = None # Set the root node format to default to source format if isinstance(self._item, hiero.core.Sequence) or self._collate: reformat = self._sequence.format().addToNukeScript(None) elif isinstance(self._item, hiero.core.TrackItem): reformat = self._clip.format().addToNukeScript(None) if isinstance(self._item, hiero.core.TrackItem): rootNode.addInputTextKnob( "shot_guid", value=hiero.core.FnNukeHelpers._guidFromCloneTag(self._item), tooltip= "This is used to identify the master track item within the script" ) inHandle, outHandle = self.outputHandles(self._retime != True) rootNode.addInputTextKnob("in_handle", value=int(inHandle)) rootNode.addInputTextKnob("out_handle", value=int(outHandle)) # This sets the format knob of the root node in the Nuke Script rootReformat = None if isinstance(self._item, hiero.core.Sequence) or self._collate: rootReformat = self._sequence.format().addToNukeScript(None) elif isinstance(self._item, hiero.core.TrackItem): rootReformat = self._item.parentSequence().format( ).addToNukeScript(None) rootNode.setKnob("format", rootReformat.knob("format")) # Add Unconnected additional nodes if self._preset.properties()["additionalNodesEnabled"]: script.addNode( FnExternalRender.createAdditionalNodes( FnExternalRender.kUnconnected, self._preset.properties()["additionalNodesData"], self._item)) # Project setting for using OCIO nodes for colourspace transform useOCIONodes = self._project.lutUseOCIOForExport() # To add Write nodes, we get a task for the paths with the preset # (default is the "Nuke Write Node" preset) and ask it to generate the Write node for # us, since it knows all about codecs and extensions and can do the token # substitution properly for that particular item. # And doing it here rather than in taskStep out of threading paranoia. self._writeNodes = [] stackId = "ScriptEnd" self._writeNodes.append(nuke.SetNode(stackId, 0)) writePathExists = False writePaths = self._preset.properties()["writePaths"] for (itemPath, itemPreset) in self._exportTemplate.flatten(): for writePath in writePaths: if writePath == itemPath: # Generate a task on same items as this one but swap in the shot path that goes with this preset. taskData = hiero.core.TaskData( itemPreset, self._item, self._exportRoot, itemPath, self._version, self._exportTemplate, project=self._project, cutHandles=self._cutHandles, retime=self._retime, startFrame=firstFrame, resolver=self._resolver, skipOffline=self._skipOffline) task = hiero.core.taskRegistry.createTaskFromPreset( itemPreset, taskData) if hasattr(task, "nukeWriteNode"): self._writeNodes.append(nuke.PushNode(stackId)) rf = itemPreset.properties()["reformat"] # If the reformat field has been set, create a reformat node immediately before the Write. if str(rf["to_type"]) == nuke.ReformatNode.kToFormat: if "width" in rf and "height" in rf and "pixelAspect" in rf and "name" in rf and "resize" in rf: format = hiero.core.Format( rf["width"], rf["height"], rf["pixelAspect"], rf["name"]) resize = rf["resize"] reformat = format.addToNukeScript( None, resize=resize) self._writeNodes.append(reformat) else: self.setError( "reformat mode set to kToFormat but preset properties do not contain required settings." ) # Add Burnin group (if enabled) burninGroup = task.addBurninNodes(script=None) if burninGroup is not None: self._writeNodes.append(burninGroup) try: writeNode = task.nukeWriteNode( framerate, projectsettings=self._projectSettings) writeNode.setKnob("first", start) writeNode.setKnob("last", end) self._writeNodes.append(writeNode) except RuntimeError as e: # Failed to generate write node, set task error in export queue # Most likely because could not map default colourspace for format settings. self.setError(str(e)) writePathExists = True # MPLEC TODO should enforce in UI that you can't pick things that won't work. if not writePaths: # Blank preset is valid, if preset has been set and doesn't exist, report as error self.setWarning( str("NukeShotExporter: No write node destination selected")) # If this flag is True, a read node pointing at the original media will be added # If read nodes which point at export items are selected, this flag will be set False originalMediaReadNode = True useEntityRefs = self._preset.properties().get("useAssets", False) for item in [self._item]: originalMediaReadNode = True if not self._collate: # Build read nodes for selected entries in the shot template readPaths = self._preset.properties()["readPaths"] for (itemPath, itemPreset) in self._exportTemplate.flatten(): for readPath in readPaths: if itemPath == readPath: # Generate a task on same items as this one but swap in the shot path that goes with this preset. taskData = hiero.core.TaskData( itemPreset, item, self._exportRoot, itemPath, self._version, self._exportTemplate, project=self._project, cutHandles=self._cutHandles, retime=self._retime, startFrame=self._startFrame, resolver=self._resolver, skipOffline=self._skipOffline) task = hiero.core.taskRegistry.createTaskFromPreset( itemPreset, taskData) readNodePath = task.resolvedExportPath() itemStart, itemEnd = task.outputRange() itemFirstFrame = firstFrame if self._startFrame: itemFirstFrame = self._startFrame if hiero.core.isVideoFileExtension( os.path.splitext(readNodePath)[1].lower()): # Don't specify frame range when media is single file newSource = hiero.core.MediaSource( readNodePath) itemEnd = itemEnd - itemStart itemStart = 0 else: # File is image sequence, so specify frame range newSource = hiero.core.MediaSource( readNodePath + (" %i-%i" % task.outputRange())) newClip = hiero.core.Clip(newSource, itemStart, itemEnd) originalMediaReadNode = False if self._cutHandles is None: newClip.addToNukeScript( script, firstFrame=itemFirstFrame, trimmed=True, useOCIO=useOCIONodes, additionalNodesCallback=self. _buildAdditionalNodes, nodeLabel=item.parent().name(), useEntityRefs=useEntityRefs) else: # Clone track item and replace source with new clip (which may be offline) newTrackItem = hiero.core.TrackItem( item.name(), item.mediaType()) for tag in self._item.tags(): newTrackItem.addTag(tag) # Handles may not be exactly what the user specified. They may be clamped to media range inHandle, outHandle = 0, 0 if self._cutHandles: # Get the output range without handles inHandle, outHandle = task.outputHandles() hiero.core.log.debug( "in/outHandle %s %s", inHandle, outHandle) newTrackItem.setSource(newClip) # Trackitem in/out newTrackItem.setTimelineIn(item.timelineIn()) newTrackItem.setTimelineOut(item.timelineOut()) # Source in/out is the clip range less the handles. newTrackItem.setSourceIn(inHandle * -1) newTrackItem.setSourceOut((newClip.duration() - 1) - outHandle) #print "New trackitem (src/dst/clip) ", newTrackItem.sourceDuration(), newTrackItem.duration(), newClip.duration() # Add track item to nuke script newTrackItem.addToNukeScript( script, firstFrame=itemFirstFrame, includeRetimes=self._retime, retimeMethod=self._preset.properties() ["method"], startHandle=self._cutHandles, endHandle=self._cutHandles, additionalNodesCallback=self. _buildAdditionalNodes, useOCIO=useOCIONodes, nodeLabel=item.parent().name(), useEntityRefs=useEntityRefs) if originalMediaReadNode: if isinstance(self._item, hiero.core.Sequence) or self._collate: # When building a collated sequence, everything is offset by 1000 # This gives head room for shots which may go negative when transposed to a # custom start frame. This offset is negated during script generation. offset = -1000 if self._collate else 0 self._sequence.addToNukeScript( script, additionalNodesCallback=self._buildAdditionalNodes, includeRetimes=True, offset=offset, useOCIO=useOCIONodes, skipOffline=self._skipOffline, useEntityRefs=useEntityRefs) elif isinstance(self._item, hiero.core.TrackItem): clip = item.source() # Add a Read Node for this Clip. if self._cutHandles is None: clip.addToNukeScript( script, firstFrame=firstFrame, trimmed=True, additionalNodesCallback=self._buildAdditionalNodes, useOCIO=useOCIONodes, nodeLabel=item.parent().name(), useEntityRefs=useEntityRefs) else: item.addToNukeScript( script, firstFrame=firstFrame, includeRetimes=self._retime, retimeMethod=self._preset.properties()["method"], startHandle=self._cutHandles, endHandle=self._cutHandles, additionalNodesCallback=self._buildAdditionalNodes, useOCIO=useOCIONodes, nodeLabel=item.parent().name(), useEntityRefs=useEntityRefs) metadataNode = nuke.MetadataNode(metadatavalues=[( "hiero/project", self._projectName), ( "hiero/project_guid", self._project.guid()), ("hiero/shot_tag_guid", self._tag_guid)]) # Add sequence Tags to metadata metadataNode.addMetadata([('"hiero/tags/' + tag.name() + '"', tag.name()) for tag in self._sequence.tags()]) metadataNode.addMetadata([('"hiero/tags/' + tag.name() + '/note"', tag.note()) for tag in self._sequence.tags()]) # Apply timeline offset to nuke output if isinstance(self._item, hiero.core.TrackItem): if self._cutHandles is None: # Whole clip, so timecode start frame is first frame of clip timeCodeNodeStartFrame = unclampedStart else: # Exporting shot with handles, adjust timecode start frame by shot trim and handle count timeCodeNodeStartFrame = ( unclampedStart - self._item.sourceIn()) + self._cutHandles timecodeStart = self._clip.timecodeStart() else: # Exporting whole sequence/clip timeCodeNodeStartFrame = unclampedStart timecodeStart = self._item.timecodeStart() script.addNode( nuke.AddTimeCodeNode(timecodeStart=timecodeStart, fps=framerate, dropFrames=dropFrames, frame=timeCodeNodeStartFrame)) # The AddTimeCode field will insert an integer framerate into the metadata, if the framerate is floating point, we need to correct this metadataNode.addMetadata([("input/frame_rate", framerate.toFloat())]) script.addNode(metadataNode) # Generate Write nodes for nuke renders. for node in self._writeNodes: script.addNode(node) # We do this here, rather than when its created so the event happens at a # suitable time, otherwise, it's called before the Read node is created. if node.type() == "Write" and self._preset.properties( )["useAssets"]: ## @todo Assuming that we're always on a thread for now, this should be verified eventManager.blockingEvent(False, 'hieroToNukeScriptAddWrite', self._item, node.knob("file"), node, script) # add a viewer viewerNode = nuke.Node("Viewer") script.addNode(viewerNode) self._writeScript(script) # Nothing left to do, return False. return False