diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 099616ff4a..1986e2d407 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -699,6 +699,33 @@ def get_ocio_config_views(config_path): ) +def _get_config_path_from_profile_data( + data, data_type, template_data) -> dict: + """Get config path from profile data. + + Args: + data (dict[str, Any]): Profile data. + data_type (str): Profile type. + template_data (dict[str, Any]): Template data. + + Returns: + dict[str, str]: Config data with path and template. + """ + template = data[data_type] + result = StringTemplate.format_strict_template( + template, template_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 { + "path": normalized_path, + "template": template + } + + def _get_global_config_data( project_name, host_name, @@ -717,7 +744,7 @@ def _get_global_config_data( 2. Custom path to ocio config. 3. Path to 'ocioconfig' representation on product. Name of product can be defined in settings. Product name can be regex but exact match is - always preferred. + always preferred. Fallback can be defined in case no product is found. None is returned when no profile is found, when path @@ -755,19 +782,8 @@ def _get_global_config_data( profile_type = profile["type"] if profile_type in ("builtin_path", "custom_path"): - template = profile[profile_type] - result = StringTemplate.format_strict_template( - template, template_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 { - "path": normalized_path, - "template": template - } + return _get_config_path_from_profile_data( + profile, profile_type, template_data) # TODO decide if this is the right name for representation repre_name = "ocioconfig" @@ -778,7 +794,21 @@ def _get_global_config_data( return None folder_path = folder_info["path"] - product_name = profile["product_name"] + # Backward compatibility for old projects + # TODO remove in future 0.4.4 onwards + product_name = profile.get("product_name") + # TODO: this should be required after backward compatibility is removed + fallback_data = None + published_product_data = profile.get("published_product") + + if product_name is None and published_product_data is None: + log.warning("Product name or published product is missing.") + return None + + if published_product_data: + product_name = published_product_data["product_name"] + fallback_data = published_product_data["fallback"] + if folder_id is None: folder_entity = ayon_api.get_folder_by_path( project_name, folder_path, fields={"id"} @@ -798,6 +828,13 @@ def _get_global_config_data( ) } if not product_entities_by_name: + # TODO: make this required in future 0.4.4 onwards + if fallback_data: + # in case no product was found we need to use fallback + fallback_type = fallback_data["type"] + return _get_config_path_from_profile_data( + fallback_data, fallback_type, template_data + ) log.debug( f"No product entities were found for folder '{folder_path}' with" f" product name filter '{product_name}'." diff --git a/server/settings/conversion.py b/server/settings/conversion.py index f513738603..b29c827cae 100644 --- a/server/settings/conversion.py +++ b/server/settings/conversion.py @@ -4,6 +4,43 @@ from typing import Any from .publish_plugins import DEFAULT_PUBLISH_VALUES +def _convert_imageio_configs_0_4_3(overrides): + """Imageio config settings did change to profiles since 0.4.3.""" + imageio_overrides = overrides.get("imageio") or {} + + # make sure settings are already converted to profiles + if ( + "ocio_config_profiles" not in imageio_overrides + ): + return + + ocio_config_profiles = imageio_overrides["ocio_config_profiles"] + + for inx, profile in enumerate(ocio_config_profiles): + if profile["type"] != "product_name": + continue + + # create new profile + new_profile = { + "type": "published_product", + "published_product": { + "product_name": profile["product_name"], + "fallback": { + "type": "builtin_path", + "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", + }, + }, + "host_names": profile["host_names"], + "task_names": profile["task_names"], + "task_types": profile["task_types"], + "custom_path": profile["custom_path"], + "builtin_path": profile["builtin_path"], + } + + # replace old profile with new profile + ocio_config_profiles[inx] = new_profile + + def _convert_imageio_configs_0_3_1(overrides): """Imageio config settings did change to profiles since 0.3.1. .""" imageio_overrides = overrides.get("imageio") or {} @@ -82,5 +119,6 @@ def convert_settings_overrides( overrides: dict[str, Any], ) -> dict[str, Any]: _convert_imageio_configs_0_3_1(overrides) + _convert_imageio_configs_0_4_3(overrides) _conver_publish_plugins(overrides) return overrides diff --git a/server/settings/main.py b/server/settings/main.py index 0972ccdfb9..09c9bf0065 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -58,7 +58,14 @@ def _ocio_config_profile_types(): return [ {"value": "builtin_path", "label": "AYON built-in OCIO config"}, {"value": "custom_path", "label": "Path to OCIO config"}, - {"value": "product_name", "label": "Published product"}, + {"value": "published_product", "label": "Published product"}, + ] + + +def _fallback_ocio_config_profile_types(): + return [ + {"value": "builtin_path", "label": "AYON built-in OCIO config"}, + {"value": "custom_path", "label": "Path to OCIO config"}, ] @@ -76,6 +83,49 @@ def _ocio_built_in_paths(): ] +class FallbackProductModel(BaseSettingsModel): + _layout = "expanded" + type: str = SettingsField( + title="Fallback config type", + enum_resolver=_fallback_ocio_config_profile_types, + conditionalEnum=True, + default="builtin_path", + description=( + "Type of config which needs to be used in case published " + "product is not found." + ), + ) + builtin_path: str = SettingsField( + "ACES 1.2", + title="Built-in OCIO config", + enum_resolver=_ocio_built_in_paths, + description=( + "AYON ocio addon distributed OCIO config. " + "Activated addon in bundle is required: 'ayon_ocio' >= 1.1.1" + ), + ) + custom_path: str = SettingsField( + "", + title="OCIO config path", + description="Path to OCIO config. Anatomy formatting is supported.", + ) + + +class PublishedProductModel(BaseSettingsModel): + _layout = "expanded" + product_name: str = SettingsField( + "", + title="Product name", + description=( + "Context related published product name to get OCIO config from. " + "Partial match is supported via use of regex expression." + ), + ) + fallback: FallbackProductModel = SettingsField( + default_factory=FallbackProductModel, + ) + + class CoreImageIOConfigProfilesModel(BaseSettingsModel): _layout = "expanded" host_names: list[str] = SettingsField( @@ -102,19 +152,19 @@ class CoreImageIOConfigProfilesModel(BaseSettingsModel): "ACES 1.2", title="Built-in OCIO config", enum_resolver=_ocio_built_in_paths, + description=( + "AYON ocio addon distributed OCIO config. " + "Activated addon in bundle is required: 'ayon_ocio' >= 1.1.1" + ), ) custom_path: str = SettingsField( "", title="OCIO config path", description="Path to OCIO config. Anatomy formatting is supported.", ) - product_name: str = SettingsField( - "", - title="Product name", - description=( - "Published product name to get OCIO config from. " - "Partial match is supported." - ), + published_product: PublishedProductModel = SettingsField( + default_factory=PublishedProductModel, + title="Published product", ) @@ -294,7 +344,14 @@ DEFAULT_VALUES = { "type": "builtin_path", "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "custom_path": "", - "product_name": "", + "published_product": { + "product_name": "", + "fallback": { + "type": "builtin_path", + "builtin_path": "ACES 1.2", + "custom_path": "" + } + } } ], "file_rules": {