diff --git a/client/ayon_core/pipeline/colorspace.py b/client/ayon_core/pipeline/colorspace.py index 099616ff4a..8c4f97ab1c 100644 --- a/client/ayon_core/pipeline/colorspace.py +++ b/client/ayon_core/pipeline/colorspace.py @@ -699,6 +699,34 @@ def get_ocio_config_views(config_path): ) +def _get_config_path_from_profile_data( + profile, profile_type, template_data +): + """Get config path from profile data. + + Args: + profile (dict[str, Any]): Profile data. + profile_type (str): Profile type. + template_data (dict[str, Any]): Template data. + + Returns: + dict[str, str]: Config data with path and template. + """ + 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 + } + + def _get_global_config_data( project_name, host_name, @@ -717,7 +745,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,30 +783,36 @@ 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" + published_product_data = profile["published_product"] + product_name = published_product_data["product_name"] + fallback_data = published_product_data["fallback"] + + if product_name == "": + log.error( + "Colorspace OCIO config path cannot be set. " + "Profile is set to published product but `Product name` is empty." + ) + return None + folder_info = template_data.get("folder") if not folder_info: log.warning("Folder info is missing.") - return None + + log.info("Using fallback data for ocio config path.") + # in case no product was found we need to use fallback + fallback_type = fallback_data["fallback_type"] + return _get_config_path_from_profile_data( + fallback_data, fallback_type, template_data + ) + folder_path = folder_info["path"] - product_name = profile["product_name"] if folder_id is None: folder_entity = ayon_api.get_folder_by_path( project_name, folder_path, fields={"id"} @@ -797,12 +831,13 @@ def _get_global_config_data( fields={"id", "name"} ) } + if not product_entities_by_name: - log.debug( - f"No product entities were found for folder '{folder_path}' with" - f" product name filter '{product_name}'." + # 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 ) - return None # Try to use exact match first, otherwise use first available product product_entity = product_entities_by_name.get(product_name) @@ -837,6 +872,7 @@ def _get_global_config_data( path = get_representation_path_with_anatomy(repre_entity, anatomy) template = repre_entity["attrib"]["template"] + return { "path": path, "template": template, diff --git a/server/settings/conversion.py b/server/settings/conversion.py index c855863591..34820b5b32 100644 --- a/server/settings/conversion.py +++ b/server/settings/conversion.py @@ -4,6 +4,29 @@ from typing import Any from .publish_plugins import DEFAULT_PUBLISH_VALUES +def _convert_imageio_configs_0_4_5(overrides): + """Imageio config settings did change to profiles since 0.4.5.""" + imageio_overrides = overrides.get("imageio") or {} + + # make sure settings are already converted to profiles + ocio_config_profiles = imageio_overrides.get("ocio_config_profiles") + if not ocio_config_profiles: + return + + for profile in ocio_config_profiles: + if profile.get("type") != "product_name": + continue + + profile["type"] = "published_product" + profile["published_product"] = { + "product_name": profile.pop("product_name"), + "fallback": { + "type": "builtin_path", + "builtin_path": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", + }, + } + + 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 {} @@ -76,7 +99,8 @@ def _convert_oiio_transcode_0_4_5(publish_overrides): if "ExtractOIIOTranscode" not in publish_overrides: return - transcode_profiles = publish_overrides["ExtractOIIOTranscode"].get("profiles") + transcode_profiles = publish_overrides["ExtractOIIOTranscode"].get( + "profiles") if not transcode_profiles: return @@ -85,7 +109,7 @@ def _convert_oiio_transcode_0_4_5(publish_overrides): if outputs is None: return - for output in outputs : + for output in outputs: # Already new settings if "display_view" in output: break @@ -102,7 +126,7 @@ def _convert_oiio_transcode_0_4_5(publish_overrides): } -def _conver_publish_plugins(overrides): +def _convert_publish_plugins(overrides): if "publish" not in overrides: return _convert_validate_version_0_3_3(overrides["publish"]) @@ -114,5 +138,6 @@ def convert_settings_overrides( overrides: dict[str, Any], ) -> dict[str, Any]: _convert_imageio_configs_0_3_1(overrides) - _conver_publish_plugins(overrides) + _convert_imageio_configs_0_4_5(overrides) + _convert_publish_plugins(overrides) return overrides diff --git a/server/settings/main.py b/server/settings/main.py index 0972ccdfb9..249bab85fd 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" + fallback_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": { + "fallback_type": "builtin_path", + "builtin_path": "ACES 1.2", + "custom_path": "" + } + } } ], "file_rules": {