Skip to content

Commit 8cc9cff

Browse files
fix output directory handling
1 parent 05ccd0b commit 8cc9cff

3 files changed

Lines changed: 44 additions & 22 deletions

File tree

publish_gizmo/nuke_gizmo_publisher.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,15 @@ def _customize_project_yml(companion_base: Path) -> None:
233233
path_macro="griptape_outputs",
234234
)
235235

236-
workflow_name = companion_base.name
237236
template.situations["save_node_output"] = SituationTemplate(
238237
name="save_node_output",
239238
description="Node generates and saves output (Nuke gizmo)",
240-
# Drop node_name and version-index suffixes so the artist's chosen
241-
# filename is preserved exactly. {sub_dirs?:/} passes through any
242-
# relative subdirectory the artist typed (e.g. "renders/comp.exr"
243-
# lands under griptape_outputs/<workflow>/renders/comp.exr).
244-
macro=f"{{outputs}}/{workflow_name}/{{sub_dirs?:/}}{{file_name_base}}.{{file_extension}}",
239+
# No workflow-name subdirectory: files land directly under {outputs}.
240+
# When Output Directory is set at runtime, {outputs} is redirected there,
241+
# so files go directly into the artist's chosen directory.
242+
# {sub_dirs?:/} passes through any relative subdirectory the artist typed
243+
# (e.g. "renders/comp.exr" lands under griptape_outputs/renders/comp.exr).
244+
macro="{outputs}/{sub_dirs?:/}{file_name_base}.{file_extension}",
245245
policy=SituationPolicy(
246246
on_collision=SituationFilePolicy.OVERWRITE,
247247
create_dirs=True,

publish_gizmo/nuke_workflow_runner.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
import re
2727
import subprocess
2828
import sys
29+
import tempfile
2930
from pathlib import Path
3031

3132
from dotenv import load_dotenv
3233
from griptape_nodes.common.project_templates import load_project_template_from_yaml
34+
from griptape_nodes.common.project_templates.directory import DirectoryDefinition
3335
from griptape_nodes.common.project_templates.validation import ProjectValidationInfo, ProjectValidationStatus
3436
from output_protocol import emit_payload
3537

@@ -254,16 +256,50 @@ def main() -> None:
254256
sys.exit(1)
255257

256258
script_dir = Path(__file__).parent
257-
project_file = script_dir / "project.yml"
259+
bundle_project_file = script_dir / "project.yml"
260+
261+
# When an output directory is specified, build a per-run temp project.yml that
262+
# redirects {outputs} to the requested directory. The bundle's situation macro
263+
# and OVERWRITE policy are kept exactly as authored — only the directory changes.
264+
# This makes the engine's actual save path agree with the path we report to Nuke.
265+
# The bundle file itself is never modified.
266+
temp_project_file = None
267+
project_file_path: str | None = str(bundle_project_file) if bundle_project_file.exists() else None
268+
269+
if args.output_dir and bundle_project_file.exists():
270+
validation_info = ProjectValidationInfo(status=ProjectValidationStatus.GOOD)
271+
template = load_project_template_from_yaml(bundle_project_file.read_text(encoding="utf-8"), validation_info)
272+
if template is not None:
273+
template.directories["outputs"] = DirectoryDefinition(
274+
name="outputs",
275+
path_macro=args.output_dir,
276+
)
277+
tmp = tempfile.NamedTemporaryFile(
278+
mode="w",
279+
suffix=".yml",
280+
prefix="griptape_nuke_run_",
281+
delete=False,
282+
encoding="utf-8",
283+
)
284+
tmp.write(template.to_yaml())
285+
tmp.close()
286+
temp_project_file = tmp.name
287+
project_file_path = temp_project_file
258288

259289
try:
260290
output = module.execute_workflow(
261291
input=flow_input,
262-
project_file_path=str(project_file) if project_file.exists() else None,
292+
project_file_path=project_file_path,
263293
)
264294
except Exception as e:
265295
emit_payload({"error": f"Workflow execution failed: {e}"})
266296
sys.exit(1)
297+
finally:
298+
if temp_project_file is not None:
299+
try:
300+
Path(temp_project_file).unlink(missing_ok=True)
301+
except OSError:
302+
pass
267303

268304
workspace_dir = Path(args.nk_script_dir) if args.nk_script_dir else None
269305
macro_map = _build_macro_map(Path(__file__).parent, workspace_dir=workspace_dir)

publish_gizmo/run_button.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import tempfile
3434
import threading
3535
import time
36-
from pathlib import Path
3736

3837
# run_button.py is exec'd (not imported) by the gizmo bootstrap, so __file__'s
3938
# directory is not added to sys.path automatically. Insert it so that
@@ -306,19 +305,6 @@ def _uv_fallback_paths():
306305
else:
307306
print(f"[Griptape] knob '{_k}' -> NOT FOUND on node")
308307

309-
# -- Resolve bare filenames against output_dir --
310-
# When the artist sets an Output Directory on the gizmo and a workflow input
311-
# is a relative filename (e.g. output_file="julia_test.jpg"), combine them
312-
# into an absolute path so SaveImage bypasses the project macro and writes
313-
# exactly where the artist expects. Already-absolute paths are left alone.
314-
if output_dir:
315-
_output_dir_path = Path(output_dir)
316-
for _k in list(inputs.keys()):
317-
_v = inputs[_k]
318-
if isinstance(_v, str) and _v and not Path(_v).is_absolute():
319-
inputs[_k] = str(_output_dir_path / _v)
320-
print(f"[Griptape] resolved '{_k}' -> {inputs[_k]!r}")
321-
322308
# -- Render media inputs from upstream Nuke connections to temp files --
323309

324310
for _mk in _media_input_names:

0 commit comments

Comments
 (0)