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
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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)
Exemplo n.º 7
0
    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()
Exemplo n.º 8
0
    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)
Exemplo n.º 11
0
    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