From 722eaaf523a977130d79dd939be4f004d604c596 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Aug 2026 20:45:28 +0800 Subject: [PATCH 01/10] bugfix: add check_inventory_version into on_root_create and on_script_load --- client/ayon_nuke/api/pipeline.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_nuke/api/pipeline.py b/client/ayon_nuke/api/pipeline.py index 6bcf1d36f9..a6297970fe 100644 --- a/client/ayon_nuke/api/pipeline.py +++ b/client/ayon_nuke/api/pipeline.py @@ -226,11 +226,8 @@ def add_nuke_callbacks(project_settings: dict = None): nuke_settings = project_settings["nuke"] - # Set all workfile settings.' nuke.addOnCreate(on_root_create, nodeClass="Root") - # set checker for last versions on loaded containers - nuke.addOnScriptLoad(check_inventory_versions) - # fix ffmpeg settings on script + nuke.addOnScriptLoad(on_script_load) # set checker for last versions on loaded containers @@ -268,6 +265,8 @@ def on_root_create() -> None: workfile_settings.set_favorites() # template builder callbacks start_workfile_template_builder() + # set checker for last versions on loaded containers + check_inventory_versions() def on_script_load() -> None: @@ -298,6 +297,9 @@ def on_script_load() -> None: if on_script_load_settings["set_colorspace"]: workfile_settings.set_colorspace() + # set checker for last versions on loaded containers + check_inventory_versions() + def reload_config(): """Attempt to reload pipeline at run-time. From e726c40f1af7baedf936f5ce8eaa8b2b7a11be9f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Aug 2026 22:59:24 +0800 Subject: [PATCH 02/10] remove check_inventrory_versions function in on_root_create --- client/ayon_nuke/api/pipeline.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_nuke/api/pipeline.py b/client/ayon_nuke/api/pipeline.py index a6297970fe..451adac05a 100644 --- a/client/ayon_nuke/api/pipeline.py +++ b/client/ayon_nuke/api/pipeline.py @@ -265,8 +265,6 @@ def on_root_create() -> None: workfile_settings.set_favorites() # template builder callbacks start_workfile_template_builder() - # set checker for last versions on loaded containers - check_inventory_versions() def on_script_load() -> None: From 6eb4d5797727823a272458189c9c04c90cb69fd3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 00:40:29 +0200 Subject: [PATCH 03/10] Run certain logic only on new file, instead of also on script load --- client/ayon_nuke/api/pipeline.py | 40 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/client/ayon_nuke/api/pipeline.py b/client/ayon_nuke/api/pipeline.py index 451adac05a..100fa9d357 100644 --- a/client/ayon_nuke/api/pipeline.py +++ b/client/ayon_nuke/api/pipeline.py @@ -242,29 +242,33 @@ def add_nuke_callbacks(project_settings: dict = None): def on_root_create() -> None: - """Callback function for on script create.""" - # set apply all workfile settings on script load and save + """Callback function for on root node create.""" + # adding favorites to file browser workfile_settings = WorkfileSettings() - on_script_create_settings = ( - workfile_settings.project_settings - ["nuke"] - ["workfile_callbacks"] - ["on_script_create"] - ) + workfile_settings.set_favorites() - if on_script_create_settings["set_resolution"]: - workfile_settings.reset_resolution() + # Root created callback is also triggered on scene load because that also + # creates a new root node. So we check if current file is None to run + # logic that we only want to apply on new scene creation. + if current_file() is None: + on_script_create_settings = ( + workfile_settings.project_settings + ["nuke"] + ["workfile_callbacks"] + ["on_script_create"] + ) - if on_script_create_settings["set_frame_range"]: - workfile_settings.reset_frame_range_handles() + if on_script_create_settings["set_resolution"]: + workfile_settings.reset_resolution() - if on_script_create_settings["set_colorspace"]: - workfile_settings.set_colorspace() + if on_script_create_settings["set_frame_range"]: + workfile_settings.reset_frame_range_handles() - # adding favorites to file browser - workfile_settings.set_favorites() - # template builder callbacks - start_workfile_template_builder() + if on_script_create_settings["set_colorspace"]: + workfile_settings.set_colorspace() + + # template builder callbacks + start_workfile_template_builder() def on_script_load() -> None: From 6c8fd589e8f77f5f374f71d99429eaf2727e7521 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 00:44:41 +0200 Subject: [PATCH 04/10] Trigger on script load callback on AYON's open workfile via workfiles tool and host implementation --- client/ayon_nuke/api/workio.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index aec89b59ae..8c051a4f8f 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -30,6 +30,10 @@ def read_script(nuke_script): nuke.Root()["name"].setValue(nuke_script) nuke.Root()["project_directory"].setValue(os.path.dirname(nuke_script)) nuke.Root().setModified(False) + + # Above way of loading script does not trigger onScriptLoad() + # callback, so we need to call it manually + nuke.onScriptLoad() else: nuke.scriptOpen(nuke_script) From 79b242640f27521680c95ee3c2d571449cc21a1f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 01:09:29 +0200 Subject: [PATCH 05/10] Do not trigger on create callbacks on the brief `scriptClear()` when opening file --- client/ayon_nuke/api/workio.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index 8c051a4f8f..e9b865c758 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -1,10 +1,22 @@ """Host API required Work Files tool""" import os +import contextlib import nuke import shutil from .constants import ASSIST +@contextlib.contextmanager +def _no_script_create_callbacks(): + """Context manager to temporarily disable on_script_create callbacks.""" + callbacks = nuke.onCreates.copy() + try: + nuke.onCreates.clear() + yield + finally: + nuke.onCreates = callbacks + + def file_extensions(): return [".nk"] @@ -25,7 +37,8 @@ def open_file(filepath): def read_script(nuke_script): if not ASSIST: - nuke.scriptClear() + with _no_script_create_callbacks(): + nuke.scriptClear() nuke.scriptReadFile(nuke_script) nuke.Root()["name"].setValue(nuke_script) nuke.Root()["project_directory"].setValue(os.path.dirname(nuke_script)) From 9d93fcf0279569e7fe22dc76e69849f6c2978cd9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 01:10:32 +0200 Subject: [PATCH 06/10] Docstring --- client/ayon_nuke/api/workio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index e9b865c758..ad64a17662 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -8,7 +8,7 @@ @contextlib.contextmanager def _no_script_create_callbacks(): - """Context manager to temporarily disable on_script_create callbacks.""" + """Context manager to temporarily disable `nuke.onCreate` callbacks.""" callbacks = nuke.onCreates.copy() try: nuke.onCreates.clear() From ed5e6814f089c131f8600d0849e615d7f7aa89a2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 01:11:22 +0200 Subject: [PATCH 07/10] Better function name --- client/ayon_nuke/api/workio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index ad64a17662..d3f9eb74df 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -7,7 +7,7 @@ @contextlib.contextmanager -def _no_script_create_callbacks(): +def _no_create_callbacks(): """Context manager to temporarily disable `nuke.onCreate` callbacks.""" callbacks = nuke.onCreates.copy() try: @@ -37,7 +37,7 @@ def open_file(filepath): def read_script(nuke_script): if not ASSIST: - with _no_script_create_callbacks(): + with _no_create_callbacks(): nuke.scriptClear() nuke.scriptReadFile(nuke_script) nuke.Root()["name"].setValue(nuke_script) From b5cf96dcd907825177f9f8fe895acae0bb646d40 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 01:14:48 +0200 Subject: [PATCH 08/10] Correctly restore on create callbacks --- client/ayon_nuke/api/workio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index d3f9eb74df..489e7a6038 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -14,7 +14,7 @@ def _no_create_callbacks(): nuke.onCreates.clear() yield finally: - nuke.onCreates = callbacks + nuke.onCreates.update(callbacks) def file_extensions(): From f5fb89699f43246cdc7a73d7bd509a6229b469fa Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 01:20:37 +0200 Subject: [PATCH 09/10] Correct mimic file open callback behavior --- client/ayon_nuke/api/workio.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index 489e7a6038..564c7e7ac3 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -40,12 +40,18 @@ def read_script(nuke_script): with _no_create_callbacks(): nuke.scriptClear() nuke.scriptReadFile(nuke_script) - nuke.Root()["name"].setValue(nuke_script) - nuke.Root()["project_directory"].setValue(os.path.dirname(nuke_script)) - nuke.Root().setModified(False) - # Above way of loading script does not trigger onScriptLoad() - # callback, so we need to call it manually + root = nuke.Root() + root["name"].setValue(nuke_script) + root["project_directory"].setValue(os.path.dirname(nuke_script)) + root.setModified(False) + + # Above way of loading script does not trigger onCreate() nor + # onScriptLoad() callback, so we need to call it manually here + # after root name has been set so the callbacks behave similar + # to how they would if user would do File > Open or on Nuke + # launch with a comp script file. + nuke.onCreate() nuke.onScriptLoad() else: nuke.scriptOpen(nuke_script) From f20f8a8e46a15d34b08b21a4dc0ea35398b2d201 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 20 Aug 2026 01:38:39 +0200 Subject: [PATCH 10/10] Do not load file twice if user chooses to open the autosave, prompt the user before opening the nuke script --- client/ayon_nuke/api/workio.py | 46 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index 564c7e7ac3..28b77e4b67 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -33,6 +33,26 @@ def save_file(filepath): nuke.Root().setModified(False) +def _get_autosave_filepath(filepath: str) -> str | None: + """Query autosave file path for a given file path, by evaluating the + autosave name preferences with root name temporarily set to the given + file path. + + This allows us to get the autosave path for a file that is not currently + open in Nuke. + """ + root = nuke.Root() + original_name = root.name() + root["name"].setValue(filepath) + try: + autosave = nuke.toNode("preferences")["AutoSaveName"].evaluate() + if os.path.isfile(autosave): + return autosave + return None + finally: + root["name"].setValue(original_name) + + def open_file(filepath): def read_script(nuke_script): @@ -58,26 +78,24 @@ def read_script(nuke_script): filepath = filepath.replace("\\", "/") - # To remain in the same window, we have to clear the script and read - # in the contents of the workfile. - # Nuke Preferences can be read after the script is read. - read_script(filepath) - - if nuke.GUI: - autosave = nuke.toNode("preferences")["AutoSaveName"].evaluate() - autosave_prmpt = "Autosave detected.\n" \ - "Would you like to load the autosave file?" # noqa - if os.path.isfile(autosave) and nuke.ask(autosave_prmpt): + # Before opening the file, see if it has an autosave file and ask the user + # if they want to load it instead. + if nuke.GUI and (autosave := _get_autosave_filepath(filepath)): + if nuke.ask( + "Autosave detected.\n" + "Would you like to load the autosave file?" + ): try: # Overwrite the filepath with autosave shutil.copy(autosave, filepath) - # Now read the (auto-saved) script again - read_script(filepath) except shutil.Error as err: nuke.message( - "Detected autosave file could not be used.\n{}" + f"Detected autosave file could not be used.\n{err}" + ) - .format(err)) + # To remain in the same window, we have to clear the script and read + # in the contents of the workfile. + read_script(filepath) return True