Last active
February 9, 2024 16:43
-
-
Save tokejepsen/bec84c9ad2d19e2e4eb0d84263554cdc to your computer and use it in GitHub Desktop.
NukeStudio Frame Exporter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Place in [NUKE_PATH]/Python/StartupUI | |
""" | |
import os | |
from PySide2 import QtCore, QtWidgets | |
import hiero.core.nuke as nuke | |
import hiero.core.nuke.Node | |
import hiero.core | |
from hiero.ui.FnTaskUIFormLayout import TaskUIFormLayout | |
from hiero.ui.FnUIProperty import ( | |
UIPropertyFactory | |
) | |
class FrameExporter(hiero.exporters.FnTranscodeExporter.TranscodeExporter): | |
def __init__(self, initDict): | |
hiero.exporters.FnTranscodeExporter.TranscodeExporter.__init__( | |
self, initDict | |
) | |
# Store progress for later updating. | |
self._progress = 0.0 | |
def taskStep(self): | |
# Generate a nuke script to render. | |
self._script = nuke.ScriptWriter() | |
isTrackItem = isinstance(self._item, hiero.core.TrackItem) | |
isClip = isinstance(self._item, hiero.core.Clip) | |
isSequence = isinstance(self._item, hiero.core.Sequence) | |
assert (isTrackItem or isClip or isSequence) | |
# Export an individual clip or track item | |
if isClip or isTrackItem: | |
self.writeClipOrTrackItemToScript(self._script) | |
# Export an entire sequence | |
elif isSequence: | |
self.writeSequenceToScript(self._script) | |
# Layout the script | |
hiero.exporters.FnScriptLayout.scriptLayout(self._script) | |
frames = self.get_export_frames() | |
for frame in frames: | |
self.export_frame(frame) | |
self._progress = 1.0 | |
return False | |
def export_frame(self, frame): | |
for node in self._script.getNodes(): | |
# Adjusting the root settings. | |
if node.__class__.__name__ == "RootNode": | |
node.knobs()["first_frame"] = frame | |
node.knobs()["last_frame"] = frame | |
if isinstance(self._item, hiero.core.TrackItem): | |
if node.__class__.__name__ == "TimeClipNode": | |
node.knobs()["frame"] = frame | |
node.knobs()["first"] = ( | |
self._item.mapTimelineToSource(frame) | |
) | |
node.knobs()["last"] = ( | |
self._item.mapTimelineToSource(frame) | |
) | |
self._script.writeToDisk(self._scriptfile) | |
log_path = self._scriptfile.replace(".nk", ".log") | |
log_file = open(log_path, "w") | |
process = nuke.executeNukeScript(self._scriptfile, log_file, True) | |
self.poll(process) | |
log_file.close() | |
if not self._preset.properties()["keepNukeScript"]: | |
os.remove(self._scriptfile) | |
os.remove(log_path) | |
def poll(self, process): | |
import time | |
returnCode = process.poll() | |
# if the return code hasn't been set, Nuke is still running | |
if returnCode is None: | |
time.sleep(1) | |
self.poll(process) | |
def startTask(self): | |
pass | |
def finishTask(self): | |
pass | |
def progress(self): | |
return self._progress | |
def get_export_frames(self): | |
item = self._item | |
# Can not tag TrackItems so will be searching the sequence for tagged | |
# frames. | |
if isinstance(self._item, hiero.core.TrackItem): | |
item = self._item.sequence() | |
frames_to_export = [] | |
for tag in item.tags(): | |
data = tag.metadata().dict() | |
if "tag.length" in data and data["tag.length"] == "1": | |
frames_to_export.append(int(data["tag.start"])) | |
# Can not tag TrackItems, so TrackItem will search for tagged | |
# frames in the sequence within its timeline range. | |
if isinstance(self._item, hiero.core.TrackItem): | |
frames_to_keep = [] | |
for frame in frames_to_export: | |
if frame < int(self._item.timelineIn()): | |
continue | |
if frame > int(self._item.timelineOut()): | |
continue | |
frames_to_keep.append(frame) | |
frames_to_export = frames_to_keep | |
return frames_to_export | |
class FrameExportUI(hiero.exporters.FnExternalRenderUI.NukeRenderTaskUI): | |
def __init__(self, preset): | |
"""Initialize""" | |
hiero.exporters.FnExternalRenderUI.NukeRenderTaskUI.__init__( | |
self, | |
preset, | |
FrameExporter, | |
"Frame Exporter" | |
) | |
self._tags = [] | |
def populateUI(self, widget, exportTemplate): | |
layout = widget.layout() | |
formLayout = TaskUIFormLayout() | |
self._formLayout = formLayout | |
layout.addLayout(formLayout) | |
self.buildCodecUI(formLayout, itemTaskType=self.taskItemType()) | |
# Effects | |
formLayout.addDivider("Effects") | |
includeEffectsToolTip = ( | |
"Enable this to include soft effects in the exported script." | |
) | |
key, value, label = "includeEffects", True, "Include Effects" | |
uiProperty = UIPropertyFactory.create( | |
type(value), | |
key=key, | |
value=value, | |
dictionary=self._preset.properties(), | |
label=label+":", | |
tooltip=includeEffectsToolTip | |
) | |
formLayout.addRow(label+":", uiProperty) | |
self._uiProperties.append(uiProperty) | |
# BURN-IN | |
formLayout.addDivider("Burn-in") | |
burninToolTip = ( | |
"When enabled, a text burn-in is applied to the media using a " | |
"Nuke Gizmo.\n" | |
"Click Edit to define the information applied during burn-in. " | |
"Burn-in fields accept any combination of dropdown tokens and " | |
"custom text, for example {clip}_myEdit.\n" | |
"You can also include Nuke expression syntax, for example " | |
"[metadata input/ctime], will add the creation time metadata in " | |
"the Nuke stream." | |
) | |
burninLayout = QtWidgets.QHBoxLayout() | |
burninCheckbox = QtWidgets.QCheckBox() | |
burninCheckbox.setToolTip(burninToolTip) | |
burninCheckbox.stateChanged.connect(self._burninEnableClicked) | |
if self._preset.properties()["burninDataEnabled"]: | |
burninCheckbox.setCheckState(QtCore.Qt.Checked) | |
burninButton = QtWidgets.QPushButton("Edit") | |
burninButton.setToolTip(burninToolTip) | |
burninButton.clicked.connect(self._burninEditClicked) | |
burninLayout.addWidget(burninCheckbox) | |
burninLayout.addWidget(burninButton) | |
formLayout.addRow("Burn-in Gizmo:", burninLayout) | |
# NUKE SCRIPT | |
# create checkbox for whether the EDL task should add Absolute Paths | |
formLayout.addDivider("Nuke Script") | |
keepNukeScriptCheckbox = QtWidgets.QCheckBox() | |
keepNukeScriptCheckbox.setCheckState(QtCore.Qt.Unchecked) | |
if self._preset.properties()["keepNukeScript"]: | |
keepNukeScriptCheckbox.setCheckState(QtCore.Qt.Checked) | |
keepNukeScriptCheckbox.stateChanged.connect( | |
self.keepNukeScriptCheckboxChanged | |
) | |
keepNukeScriptCheckbox.setToolTip( | |
"A Nuke script is created for each transcode. If you'd like to " | |
"keep the temporary .nk file from being destroyed, enable this " | |
"option. The script will get generated into the same directory as " | |
"the transcode output" | |
) | |
formLayout.addRow("Keep Nuke Script:", keepNukeScriptCheckbox) | |
class FramePreset(hiero.exporters.FnTranscodeExporter.TranscodePreset): | |
def __init__(self, name, properties): | |
hiero.core.RenderTaskPreset.__init__( | |
self, FrameExporter, name, properties | |
) | |
# Set any preset defaults here | |
self.properties()["keepNukeScript"] = False | |
self.properties()["useSingleSocket"] = False | |
self.properties()["burninDataEnabled"] = False | |
burninPropertyData = ( | |
hiero.exporters.FnExternalRender.NukeRenderTask.burninPropertyData | |
) | |
self.properties()["burninData"] = dict( | |
(datadict["knobName"], None) for datadict in burninPropertyData | |
) | |
self.properties()["additionalNodesEnabled"] = False | |
self.properties()["additionalNodesData"] = [] | |
self.properties()["method"] = "Blend" | |
self.properties()["includeEffects"] = True | |
self.properties()["includeAudio"] = False | |
self.properties()["deleteAudio"] = True | |
self.properties()["readAllLinesForExport"] = False | |
# Give the Write node a name, so it can be referenced elsewhere | |
if "writeNodeName" not in self.properties(): | |
self.properties()["writeNodeName"] = "Write_{ext}" | |
self.properties().update(properties) | |
hiero.ui.taskUIRegistry.registerTaskUI(FramePreset, FrameExportUI) | |
hiero.core.taskRegistry.registerTask(FramePreset, FrameExporter) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TIL from the foundry forums (https://community.foundry.com/discuss/topic/147525/nukestudio-export-tagged-frames#1188902):