From 862049d995087d163ac02cb2c2538dcc53dffe38 Mon Sep 17 00:00:00 2001 From: timsergeeff <38128238+timsergeeff@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:00:09 +0300 Subject: [PATCH 01/26] Refactor color conversion logic in transcoding.py --- client/ayon_core/lib/transcoding.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 127bd3bac4..12a64c7e06 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1170,6 +1170,14 @@ def oiio_color_convert( # Handle the different conversion cases # Source view and display are known if source_view and source_display: + color_convert_args = None + ocio_display_args = None + oiio_cmd.extend([ + "--ociodisplay:inverse=1:subimages=0", + source_display, + source_view + ]) + if target_colorspace: # This is a two-step conversion process since there's no direct # display/view to colorspace command @@ -1179,22 +1187,28 @@ def oiio_color_convert( elif source_display != target_display or source_view != target_view: # Complete display/view pair conversion # - go through a reference space - color_convert_args = (target_display, target_view) + ocio_display_args = (target_display, target_view) else: color_convert_args = None + ocio_display_args = None logger.debug( "Source and target display/view pairs are identical." " No color conversion needed." ) + if color_convert_args: + # Use colorconvert for colorspace target oiio_cmd.extend([ - "--ociodisplay:inverse=1:subimages=0", - source_display, - source_view, "--colorconvert:subimages=0", *color_convert_args ]) + elif ocio_display_args: + # Use ociodisplay for display/view target + oiio_cmd.extend([ + "--ociodisplay:subimages=0", + *ocio_display_args + ]) elif target_colorspace: # Standard color space to color space conversion From 0db3f67eb3bdb04b84b8818d52971f251af1cc8e Mon Sep 17 00:00:00 2001 From: timsergeeff <38128238+timsergeeff@users.noreply.github.com> Date: Fri, 10 Oct 2025 15:06:51 +0300 Subject: [PATCH 02/26] Remove unnecessary blank lines in transcoding.py --- client/ayon_core/lib/transcoding.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 12a64c7e06..9216c88ed2 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1196,7 +1196,6 @@ def oiio_color_convert( " No color conversion needed." ) - if color_convert_args: # Use colorconvert for colorspace target oiio_cmd.extend([ @@ -1232,7 +1231,6 @@ def oiio_color_convert( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) - def split_cmd_args(in_args): """Makes sure all entered arguments are separated in individual items. From 7ef330c3f4fb0e59af9d2a58a2470a2230bd23c2 Mon Sep 17 00:00:00 2001 From: timsergeeff <38128238+timsergeeff@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:55:12 +0300 Subject: [PATCH 03/26] Update client/ayon_core/lib/transcoding.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/lib/transcoding.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 9216c88ed2..fcf7fdece2 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1173,10 +1173,10 @@ def oiio_color_convert( color_convert_args = None ocio_display_args = None oiio_cmd.extend([ - "--ociodisplay:inverse=1:subimages=0", - source_display, - source_view - ]) + "--ociodisplay:inverse=1:subimages=0", + source_display, + source_view, + ]) if target_colorspace: # This is a two-step conversion process since there's no direct From 2541f8909e6625e710e376e4dc4c10a21a7db082 Mon Sep 17 00:00:00 2001 From: timsergeeff <38128238+timsergeeff@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:55:18 +0300 Subject: [PATCH 04/26] Update client/ayon_core/lib/transcoding.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/lib/transcoding.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index fcf7fdece2..70a8f26cf2 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1189,8 +1189,6 @@ def oiio_color_convert( # - go through a reference space ocio_display_args = (target_display, target_view) else: - color_convert_args = None - ocio_display_args = None logger.debug( "Source and target display/view pairs are identical." " No color conversion needed." From aabd9f7f505ddd2972bf2fa9afd5d28318b36299 Mon Sep 17 00:00:00 2001 From: timsergeeff <38128238+timsergeeff@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:55:23 +0300 Subject: [PATCH 05/26] Update client/ayon_core/lib/transcoding.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/lib/transcoding.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 70a8f26cf2..37fcb59ab3 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1229,6 +1229,7 @@ def oiio_color_convert( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + def split_cmd_args(in_args): """Makes sure all entered arguments are separated in individual items. From 2fe89c4b4619d01b53f0bcdbc5ef2c20b5d05c5c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 3 Nov 2025 22:09:25 +0800 Subject: [PATCH 06/26] add substance painter as host and adjust some instance data so that it can be used to review for image product --- client/ayon_core/plugins/publish/extract_review.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 580aa27eef..665d031d5a 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -163,7 +163,8 @@ class ExtractReview(pyblish.api.InstancePlugin): "flame", "unreal", "batchdelivery", - "photoshop" + "photoshop", + "substancepainter", ] settings_category = "core" @@ -571,7 +572,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # review output files "timecode": frame_to_timecode( frame=temp_data.frame_start_handle, - fps=float(instance.data["fps"]) + fps=float(instance.data.get("fps", 25.0)) ) }) @@ -664,8 +665,8 @@ class ExtractReview(pyblish.api.InstancePlugin): with values may be added. """ - frame_start = instance.data["frameStart"] - frame_end = instance.data["frameEnd"] + frame_start = instance.data.get("frameStart", 1) + frame_end = instance.data.get("frameEnd", 1) # Try to get handles from instance handle_start = instance.data.get("handleStart") @@ -725,7 +726,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ext = os.path.splitext(repre["files"])[1].replace(".", "") return TempData( - fps=float(instance.data["fps"]), + fps=float(instance.data.get("fps", 25.0)), frame_start=frame_start, frame_end=frame_end, handle_start=handle_start, From cfed4afaaf7e22741b463fe77fb2eb3affaf7156 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Nov 2025 15:30:33 +0800 Subject: [PATCH 07/26] use the frame range from context data if it cannot find one --- client/ayon_core/plugins/publish/extract_review.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 665d031d5a..e519a4a97d 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -572,7 +572,9 @@ class ExtractReview(pyblish.api.InstancePlugin): # review output files "timecode": frame_to_timecode( frame=temp_data.frame_start_handle, - fps=float(instance.data.get("fps", 25.0)) + fps=float(instance.data.get( + "fps", instance.context.data["fps"] + )) ) }) @@ -665,8 +667,12 @@ class ExtractReview(pyblish.api.InstancePlugin): with values may be added. """ - frame_start = instance.data.get("frameStart", 1) - frame_end = instance.data.get("frameEnd", 1) + frame_start = instance.data.get( + "frameStart", instance.context.data["frameStart"] + ) + frame_end = instance.data.get( + "frameEnd", instance.context.data["frameEnd"] + ) # Try to get handles from instance handle_start = instance.data.get("handleStart") @@ -726,7 +732,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ext = os.path.splitext(repre["files"])[1].replace(".", "") return TempData( - fps=float(instance.data.get("fps", 25.0)), + fps=float(instance.data.get("fps", instance.context.data["fps"])), frame_start=frame_start, frame_end=frame_end, handle_start=handle_start, From 5ab274aa503cecc07a7289175b5c61f755c0aede Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Nov 2025 17:47:49 +0800 Subject: [PATCH 08/26] restore the instance data and adjust them into textureset collector in substance instead --- client/ayon_core/plugins/publish/extract_review.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index e519a4a97d..3f2a0dcd3e 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -572,9 +572,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # review output files "timecode": frame_to_timecode( frame=temp_data.frame_start_handle, - fps=float(instance.data.get( - "fps", instance.context.data["fps"] - )) + fps=float(instance.data["fps"]) ) }) @@ -667,12 +665,8 @@ class ExtractReview(pyblish.api.InstancePlugin): with values may be added. """ - frame_start = instance.data.get( - "frameStart", instance.context.data["frameStart"] - ) - frame_end = instance.data.get( - "frameEnd", instance.context.data["frameEnd"] - ) + frame_start = instance.data["frameStart"] + frame_end = instance.data["frameEnd"] # Try to get handles from instance handle_start = instance.data.get("handleStart") @@ -732,7 +726,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ext = os.path.splitext(repre["files"])[1].replace(".", "") return TempData( - fps=float(instance.data.get("fps", instance.context.data["fps"])), + fps=float(instance.data["fps"]), frame_start=frame_start, frame_end=frame_end, handle_start=handle_start, From c0fd2aa8c57b5ef17cfe38245f74f0b889243e51 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Nov 2025 18:10:52 +0800 Subject: [PATCH 09/26] add additional default settings into ExtractReview for substance painter --- server/settings/publish_plugins.py | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index ee422a0acf..311b4672cf 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1448,6 +1448,105 @@ DEFAULT_PUBLISH_VALUES = { "fill_missing_frames": "closest_existing" } ] + }, + { + "product_types": [], + "hosts": ["substancepainter"], + "task_types": [], + "outputs": [ + { + "name": "png", + "ext": "png", + "tags": [ + "ftrackreview", + "kitsureview", + "webreview" + ], + "burnins": [], + "ffmpeg_args": { + "video_filters": [], + "audio_filters": [], + "input": [], + "output": [] + }, + "filter": { + "families": [ + "render", + "review", + "ftrack" + ], + "product_names": [], + "custom_tags": [], + "single_frame_filter": "single_frame" + }, + "overscan_crop": "", + # "overscan_color": [0, 0, 0], + "overscan_color": [0, 0, 0, 0.0], + "width": 1920, + "height": 1080, + "scale_pixel_aspect": True, + "bg_color": [0, 0, 0, 0.0], + "letter_box": { + "enabled": False, + "ratio": 0.0, + "fill_color": [0, 0, 0, 1.0], + "line_thickness": 0, + "line_color": [255, 0, 0, 1.0] + }, + "fill_missing_frames": "only_rendered" + }, + { + "name": "h264", + "ext": "mp4", + "tags": [ + "burnin", + "ftrackreview", + "kitsureview", + "webreview" + ], + "burnins": [], + "ffmpeg_args": { + "video_filters": [], + "audio_filters": [], + "input": [ + "-apply_trc gamma22" + ], + "output": [ + "-pix_fmt yuv420p", + "-crf 18", + "-c:a aac", + "-b:a 192k", + "-g 1", + "-movflags faststart" + ] + }, + "filter": { + "families": [ + "render", + "review", + "ftrack" + ], + "product_names": [], + "custom_tags": [], + "single_frame_filter": "multi_frame" + }, + "overscan_crop": "", + # "overscan_color": [0, 0, 0], + "overscan_color": [0, 0, 0, 0.0], + "width": 0, + "height": 0, + "scale_pixel_aspect": True, + "bg_color": [0, 0, 0, 0.0], + "letter_box": { + "enabled": False, + "ratio": 0.0, + "fill_color": [0, 0, 0, 1.0], + "line_thickness": 0, + "line_color": [255, 0, 0, 1.0] + }, + "fill_missing_frames": "only_rendered" + } + ] } ] }, From 0dfaed53cba2a76f2463bc2fbf6c5ef585dd6dfa Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 11 Nov 2025 12:43:00 +0100 Subject: [PATCH 10/26] Fix setting display/view based on collected scene display/view if left empty in ExtractOIIOTranscode settings. `instance.data["sceneDisplay"]` and `instance.data["sceneView"]` are now intended to be set to describe the user's configured display/view inside the DCC and can still be used as fallback for the `ExtractOIIOTrancode` transcoding. For the time being the legacy `colorspaceDisplay` and `colorspaceView` instance.data keys will act as fallback for backwards compatibility to represent the scene display and view. Also see: https://github.com/ynput/ayon-core/issues/1430#issuecomment-3516459205 --- .../pipeline/farm/pyblish_functions.py | 4 +- .../publish/extract_color_transcode.py | 44 +++++++++---------- server/settings/publish_plugins.py | 4 +- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 2193e96cb1..45c9eb22dc 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -594,8 +594,8 @@ def create_instances_for_aov( additional_color_data = { "renderProducts": instance.data["renderProducts"], "colorspaceConfig": instance.data["colorspaceConfig"], - "display": instance.data["colorspaceDisplay"], - "view": instance.data["colorspaceView"] + "display": instance.data.get("sourceDisplay"), + "view": instance.data.get("sourceView") } # Get templated path from absolute config path. diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 1a2c85e597..b293bd29c3 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -87,15 +87,19 @@ class ExtractOIIOTranscode(publish.Extractor): profile_output_defs = profile["outputs"] new_representations = [] repres = instance.data["representations"] - for idx, repre in enumerate(list(repres)): - # target space, display and view might be defined upstream - # TODO: address https://github.com/ynput/ayon-core/pull/1268#discussion_r2156555474 - # Implement upstream logic to handle target_colorspace, - # target_display, target_view in other DCCs - target_colorspace = False - target_display = instance.data.get("colorspaceDisplay") - target_view = instance.data.get("colorspaceView") + scene_display = instance.data.get( + "sceneDisplay", + # Backward compatibility + instance.data.get("colorspaceDisplay") + ) + scene_view = instance.data.get( + "sceneView", + # Backward compatibility + instance.data.get("colorspaceView") + ) + + for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) if not self._repre_is_valid(repre): continue @@ -142,24 +146,18 @@ class ExtractOIIOTranscode(publish.Extractor): transcoding_type = output_def["transcoding_type"] - # NOTE: we use colorspace_data as the fallback values for - # the target colorspace. + # Set target colorspace/display/view based on transcoding type + target_colorspace = None + target_view = None + target_display = None if transcoding_type == "colorspace": - # TODO: Should we fallback to the colorspace - # (which used as source above) ? - # or should we compute the target colorspace from - # current view and display ? - target_colorspace = (output_def["colorspace"] or - colorspace_data.get("colorspace")) + target_colorspace = output_def["colorspace"] elif transcoding_type == "display_view": display_view = output_def["display_view"] - target_view = ( - display_view["view"] - or colorspace_data.get("view")) - target_display = ( - display_view["display"] - or colorspace_data.get("display") - ) + # If empty values are provided in output definition, + # fallback to scene display/view that is collected from DCC + target_view = display_view["view"] or scene_view + target_display = display_view["display"] or scene_display # both could be already collected by DCC, # but could be overwritten when transcoding diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index ee422a0acf..7311115966 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -443,7 +443,7 @@ class UseDisplayViewModel(BaseSettingsModel): title="Target Display", description=( "Display of the target transform. If left empty, the" - " source Display value will be used." + " scene Display value will be used." ) ) view: str = SettingsField( @@ -451,7 +451,7 @@ class UseDisplayViewModel(BaseSettingsModel): title="Target View", description=( "View of the target transform. If left empty, the" - " source View value will be used." + " scene View value will be used." ) ) From 80a95c19f143b4593d9ca610c2e5d55fc7feda56 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Nov 2025 23:24:08 +0100 Subject: [PATCH 11/26] Pass on `sceneDisplay` (legacy `colorspaceDisplay`) and `sceneView` (legacy `colorspaceView`) to metadata JSON. Also pass on `sourceDisplay` and `sourceView` --- client/ayon_core/pipeline/farm/pyblish_functions.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 45c9eb22dc..c220b75403 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -253,6 +253,19 @@ def create_skeleton_instance( "reuseLastVersion": data.get("reuseLastVersion", False), } + # Pass on the OCIO metadata of what the source display and view are + # so that the farm can correctly set up color management. + if "sceneDisplay" in data and "sceneView" in data: + instance_skeleton_data["sceneDisplay"] = data["sceneDisplay"] + instance_skeleton_data["sceneView"] = data["sceneView"] + elif "colorspaceDisplay" in data and "colorspaceView" in data: + # Backwards compatibility for sceneDisplay and sceneView + instance_skeleton_data["colorspaceDisplay"] = data["colorspaceDisplay"] + instance_skeleton_data["colorspaceView"] = data["colorspaceView"] + if "sourceDisplay" in data and "sourceView" in data: + instance_skeleton_data["sourceDisplay"] = data["sourceDisplay"] + instance_skeleton_data["sourceView"] = data["sourceView"] + if data.get("renderlayer"): instance_skeleton_data["renderlayer"] = data["renderlayer"] From 58432ff4dd3aad7b879e72a6dcf158dce381d874 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Nov 2025 23:55:12 +0100 Subject: [PATCH 12/26] Re-show 'initialize as' attribute for USD publish so it's clear what is going on with the initial layer. --- .../plugins/publish/extract_usd_layer_contributions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py index 9db8c49a02..4dec4d8b9b 100644 --- a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py +++ b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py @@ -494,7 +494,7 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, "asset" if profile.get("contribution_target_product") == "usdAsset" else "shot") - init_as_visible = False + init_as_visible = True # Attributes logic publish_attributes = instance["publish_attributes"].get( @@ -828,6 +828,7 @@ class ExtractUSDAssetContribution(publish.Extractor): # If no existing publish of this product exists then we initialize # the layer as either a default asset or shot structure. init_type = instance.data["contribution_target_product_init"] + self.log.debug("Initializing layer as type: %s", init_type) asset_layer, payload_layer = self.init_layer( asset_name=folder_name, init_type=init_type ) From 2aa7e46c9c94e5224c74c043971749f9b3c8f671 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Nov 2025 23:58:09 +0100 Subject: [PATCH 13/26] Cosmetic type hints --- .../plugins/publish/extract_usd_layer_contributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py index 4dec4d8b9b..d73a417f16 100644 --- a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py +++ b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py @@ -910,7 +910,7 @@ class ExtractUSDAssetContribution(publish.Extractor): payload_layer.Export(payload_path, args={"format": "usda"}) self.add_relative_file(instance, payload_path) - def init_layer(self, asset_name, init_type): + def init_layer(self, asset_name: str, init_type: str): """Initialize layer if no previous version exists""" if init_type == "asset": From ba6a9bdca4a8c301c8bf8122f4a0bd9afa620a46 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 27 Nov 2025 00:41:34 +0100 Subject: [PATCH 14/26] Allow OCIO color management profiles that have no matching profile or do match a profile with "disabled" status to be considered as NOT color managed. So that you can specify a particular part of the project to NOT be OCIO color managed. --- client/ayon_core/pipeline/colorspace.py | 56 ++++++++++++++++--------- server/settings/main.py | 1 + 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 41241e17ca..db7d287cf1 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -7,6 +7,7 @@ import platform import tempfile import warnings from copy import deepcopy +from dataclasses import dataclass import ayon_api @@ -25,6 +26,17 @@ from ayon_core.pipeline.load import get_representation_path_with_anatomy log = Logger.get_logger(__name__) +@dataclass +class ConfigData: + """OCIO Config to use in a certain context. + + When enabled and no path/template are set, it will be considered invalid + and will error on OCIO path not found. Enabled must be False to explicitly + allow OCIO to be disabled.""" + path: str = "" + template: str = "" + enabled: bool = True + class CachedData: remapping = {} @@ -710,7 +722,7 @@ def _get_config_path_from_profile_data( template_data (dict[str, Any]): Template data. Returns: - dict[str, str]: Config data with path and template. + ConfigData: Config data with path and template. """ template = profile[profile_type] result = StringTemplate.format_strict_template( @@ -719,12 +731,12 @@ def _get_config_path_from_profile_data( normalized_path = str(result.normalized()) if not os.path.exists(normalized_path): log.warning(f"Path was not found '{normalized_path}'.") - return None + return ConfigData() # Return invalid config data - return { - "path": normalized_path, - "template": template - } + return ConfigData( + path=normalized_path, + template=template + ) def _get_global_config_data( @@ -735,7 +747,7 @@ def _get_global_config_data( imageio_global, folder_id, log, -): +) -> ConfigData: """Get global config data. Global config from core settings is using profiles that are based on @@ -759,8 +771,7 @@ def _get_global_config_data( log (logging.Logger): Logger object. Returns: - Union[dict[str, str], None]: Config data with path and template - or None. + ConfigData: Config data with path and template. """ task_name = task_type = None @@ -779,12 +790,14 @@ def _get_global_config_data( ) if profile is None: log.info(f"No config profile matched filters {str(filter_values)}") - return None + return ConfigData(enabled=False) profile_type = profile["type"] - if profile_type in ("builtin_path", "custom_path"): + if profile_type in {"builtin_path", "custom_path"}: return _get_config_path_from_profile_data( profile, profile_type, template_data) + elif profile_type == "disabled": + return ConfigData(enabled=False) # TODO decide if this is the right name for representation repre_name = "ocioconfig" @@ -798,7 +811,7 @@ def _get_global_config_data( "Colorspace OCIO config path cannot be set. " "Profile is set to published product but `Product name` is empty." ) - return None + return ConfigData() folder_info = template_data.get("folder") if not folder_info: @@ -819,7 +832,7 @@ def _get_global_config_data( ) if not folder_entity: log.warning(f"Folder entity '{folder_path}' was not found..") - return None + return ConfigData() folder_id = folder_entity["id"] product_entities_by_name = { @@ -855,7 +868,7 @@ def _get_global_config_data( log.info( f"Product '{product_name}' does not have available any versions." ) - return None + return ConfigData() # Find 'ocioconfig' representation entity repre_entity = ayon_api.get_representation_by_name( @@ -868,15 +881,15 @@ def _get_global_config_data( f"Representation '{repre_name}'" f" not found on product '{product_name}'." ) - return None + return ConfigData() path = get_representation_path_with_anatomy(repre_entity, anatomy) template = repre_entity["attrib"]["template"] - return { - "path": path, - "template": template, - } + return ConfigData( + path=path, + template=template + ) def get_imageio_config_preset( @@ -1015,7 +1028,10 @@ def get_imageio_config_preset( host_ocio_config["filepath"], template_data ) - if not config_data: + if not config_data.enabled: + return {} # OCIO management disabled + + if not config_data.path: raise FileExistsError( "No OCIO config found in settings. It is" " either missing or there is typo in path inputs" diff --git a/server/settings/main.py b/server/settings/main.py index cca885303f..3bd9549116 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -59,6 +59,7 @@ def _ocio_config_profile_types(): {"value": "builtin_path", "label": "AYON built-in OCIO config"}, {"value": "custom_path", "label": "Path to OCIO config"}, {"value": "published_product", "label": "Published product"}, + {"value": "disabled", "label": "Disable OCIO management"}, ] From ab8a93b4a4455ba1ecf639f00d325051bb8bf8de Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 27 Nov 2025 00:57:46 +0100 Subject: [PATCH 15/26] Cosmetics --- client/ayon_core/pipeline/colorspace.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index db7d287cf1..a7d205d48e 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -26,6 +26,7 @@ from ayon_core.pipeline.load import get_representation_path_with_anatomy log = Logger.get_logger(__name__) + @dataclass class ConfigData: """OCIO Config to use in a certain context. From a73d8f947d25f6243f293204a1405f1e47065fef Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 27 Nov 2025 01:01:51 +0100 Subject: [PATCH 16/26] Fix return value --- client/ayon_core/pipeline/colorspace.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index a7d205d48e..7a4d9dda50 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -1038,7 +1038,10 @@ def get_imageio_config_preset( " either missing or there is typo in path inputs" ) - return config_data + return { + "path": config_data.path, + "template": config_data.template, + } def _get_host_config_data(templates, template_data): From fd1b3b0e64ea1516139608659ee9b013f82d372f Mon Sep 17 00:00:00 2001 From: TobiasPharos Date: Thu, 2 Oct 2025 11:22:25 +0200 Subject: [PATCH 17/26] fix for "replace_with_published_scene_path" Published scene file has to be of productType/family "workfile" --- client/ayon_core/pipeline/publish/lib.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 1f983808b0..3756746ee9 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -812,7 +812,20 @@ def replace_with_published_scene_path(instance, replace_in_path=True): template_data["comment"] = None anatomy = instance.context.data["anatomy"] - template = anatomy.get_template_item("publish", "default", "path") + project_name = anatomy.project_name + task_entity = instance.data.get("taskEntity", {}) + project_settings = get_project_settings(project_name) + template_name = get_publish_template_name( + project_name=project_name, + host_name=instance.context.data.get("hostName", os.environ["AYON_HOST_NAME"]), + # publish template has to match productType "workfile", + # otherwise default template will be used: + product_type="workfile", + task_name=task_entity.get("name", os.environ["AYON_TASK_NAME"]), + task_type=task_entity.get("taskType"), + project_settings=project_settings, + ) + template = anatomy.get_template_item("publish", template_name, "path") template_filled = template.format_strict(template_data) file_path = os.path.normpath(template_filled) From 81fb1e73c4f5c4776ad133e7ad488afea54ea973 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 17 Oct 2025 14:14:52 +0200 Subject: [PATCH 18/26] handle project settings and task entity --- client/ayon_core/pipeline/publish/lib.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 3756746ee9..555f4b1894 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -813,16 +813,20 @@ def replace_with_published_scene_path(instance, replace_in_path=True): anatomy = instance.context.data["anatomy"] project_name = anatomy.project_name - task_entity = instance.data.get("taskEntity", {}) - project_settings = get_project_settings(project_name) + task_name = task_type = None + task_entity = instance.data.get("taskEntity") + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] + project_settings = instance.context.data["project_settings"] template_name = get_publish_template_name( project_name=project_name, - host_name=instance.context.data.get("hostName", os.environ["AYON_HOST_NAME"]), + host_name=instance.context.data["hostName"], # publish template has to match productType "workfile", # otherwise default template will be used: product_type="workfile", - task_name=task_entity.get("name", os.environ["AYON_TASK_NAME"]), - task_type=task_entity.get("taskType"), + task_name=task_name, + task_type=task_type, project_settings=project_settings, ) template = anatomy.get_template_item("publish", template_name, "path") From c33795b68a6d6bd374428c07e623a23e99861a45 Mon Sep 17 00:00:00 2001 From: TobiasPharos Date: Tue, 25 Nov 2025 10:07:18 +0100 Subject: [PATCH 19/26] get product_type from workfile instance --- client/ayon_core/pipeline/publish/lib.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 555f4b1894..2187ef0304 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -822,9 +822,7 @@ def replace_with_published_scene_path(instance, replace_in_path=True): template_name = get_publish_template_name( project_name=project_name, host_name=instance.context.data["hostName"], - # publish template has to match productType "workfile", - # otherwise default template will be used: - product_type="workfile", + product_type=workfile_instance.data["productType"], task_name=task_name, task_type=task_type, project_settings=project_settings, From 8a0e1afcb37ef0843e1ae9bf714dfa949eb3bab0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 28 Nov 2025 23:16:02 +0100 Subject: [PATCH 20/26] Remove unused function: `split_cmd_args` --- client/ayon_core/lib/transcoding.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 076ee79665..726ea62542 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1281,24 +1281,6 @@ def oiio_color_convert( run_subprocess(oiio_cmd, logger=logger) -def split_cmd_args(in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - Args: - in_args (list): of arguments ['-n', '-d uint10'] - Returns - (list): ['-n', '-d', 'unint10'] - """ - splitted_args = [] - for arg in in_args: - if not arg.strip(): - continue - splitted_args.extend(arg.split(" ")) - return splitted_args - - def get_rescaled_command_arguments( application, input_path, From 72249691801ac1784e1bf85a2b070c62a03ccfa5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Sat, 29 Nov 2025 16:13:55 +0100 Subject: [PATCH 21/26] fix product name template filtering --- client/ayon_core/pipeline/create/product_name.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index ecffa4a340..5596cec0ce 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -41,8 +41,8 @@ def get_product_name_template( profiles = tools_settings["creator"]["product_name_profiles"] filtering_criteria = { "product_types": product_type, - "hosts": host_name, - "tasks": task_name, + "host_names": host_name, + "task_names": task_name, "task_types": task_type } From 930454ad08832328e98c0b8d2473eafbb3623c75 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 30 Nov 2025 21:38:36 +0100 Subject: [PATCH 22/26] Fix missing settings conversion --- server/settings/conversion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/settings/conversion.py b/server/settings/conversion.py index 846b91edab..757818a9ff 100644 --- a/server/settings/conversion.py +++ b/server/settings/conversion.py @@ -164,5 +164,6 @@ def convert_settings_overrides( ) -> dict[str, Any]: _convert_imageio_configs_0_3_1(overrides) _convert_imageio_configs_0_4_5(overrides) + _convert_imageio_configs_1_6_5(overrides) _convert_publish_plugins(overrides) return overrides From b0d153ce8745d304108468d78c5ab630f28b0255 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:30:22 +0100 Subject: [PATCH 23/26] remove python from pyproject toml --- client/pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/client/pyproject.toml b/client/pyproject.toml index c98591b707..5ae71de18b 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -3,7 +3,6 @@ name="core" description="AYON core addon." [tool.poetry.dependencies] -python = ">=3.9.1,<3.10" markdown = "^3.4.1" clique = "1.6.*" jsonschema = "^2.6.0" From 79aa108da794214285c98eea05a6c8e43b6b71aa Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 1 Dec 2025 12:02:25 +0000 Subject: [PATCH 24/26] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index a3e1a6c939..168eaa7c21 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.6.11+dev" +__version__ = "1.6.12" diff --git a/package.py b/package.py index 62231060f0..0b1bb92a3a 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.6.11+dev" +version = "1.6.12" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index d568edefc0..7b2e3f9e7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.6.11+dev" +version = "1.6.12" description = "" authors = ["Ynput Team "] readme = "README.md" From 0e34fb6474d0f30f3b164ac0579c61edf7bcb41c Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 1 Dec 2025 12:03:02 +0000 Subject: [PATCH 25/26] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 168eaa7c21..3e6b3794b1 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.6.12" +__version__ = "1.6.12+dev" diff --git a/package.py b/package.py index 0b1bb92a3a..fbf7021b8e 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.6.12" +version = "1.6.12+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 7b2e3f9e7b..208bbec85f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.6.12" +version = "1.6.12+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 31b65f22ae9860dbe02209e5914adbdc3c698097 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 1 Dec 2025 12:03:58 +0000 Subject: [PATCH 26/26] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 7fc253b1b8..98e4b46e07 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to AYON Tray options: + - 1.6.12 - 1.6.11 - 1.6.10 - 1.6.9