Adds explicit resolution override to publisher

Adds a plugin that allows users to explicitly override the resolution settings (width, height, pixel aspect) of instances during the publishing process.

This provides a way to ensure consistency and accuracy in resolution values across different tasks and product types.

The plugin is configurable through the AYON settings, allowing administrators to define the available resolution options and the product types for which the override is enabled.
This commit is contained in:
Jakub Jezek 2025-04-23 10:25:51 +02:00
parent c1d7cde34d
commit 71dc3650ac
No known key found for this signature in database
GPG key ID: 06DBD609ADF27FD9
2 changed files with 195 additions and 1 deletions

View file

@ -0,0 +1,104 @@
import pyblish.api
from ayon_core.lib import EnumDef
from ayon_core.pipeline import colorspace
from ayon_core.pipeline import publish
from ayon_core.pipeline.publish import PublishError
class CollectExplicitResolution(
pyblish.api.InstancePlugin,
publish.AYONPyblishPluginMixin
):
"""Collect explicit user defined resolution attributes for instances"""
label = "Choose Explicit Resolution"
order = pyblish.api.CollectorOrder + 0.49
settings_category = "core"
enabled = False
default_resolution_item = (None, "Don't override")
# Settings
product_types = []
options = []
# caching resoluton items
resolution_items = None
def process(self, instance):
"""Process the instance and collect explicit resolution attributes"""
# Get the values from the instance data
values = self.get_attr_values_from_data(instance.data)
resolution_value = values.get("explicit_resolution", None)
if resolution_value is None:
return
# Get the width, height and pixel_aspect from the resolution value
resolution_data = self._get_resolution_values(resolution_value)
# Set the values to the instance data
instance.data.update(resolution_data)
def _get_resolution_values(self, resolution_value):
"""
Returns width, height and pixel_aspect from the resolution value
Arguments:
resolution_value (str): resolution value
Returns:
dict: dictionary with width, height and pixel_aspect
"""
resolution_items = self._get_resolution_items()
item_values = None
# check if resolution_value is in cached items
if resolution_value in resolution_items:
item_values = resolution_items[resolution_value]
if item_values:
# if the item is in the cache, get the values from it
return {
"resolutionWidth": item_values["width"],
"resolutionHeight": item_values["height"],
"pixelAspect": item_values["pixel_aspect"]
}
else:
raise PublishError(
f"Invalid resolution value: {resolution_value}")
@classmethod
def _get_resolution_items(cls):
if cls.resolution_items is None:
resolution_items = {}
for item in cls.options:
item_text = f"{item['width']}x{item['height']}x{item['pixel_aspect']}"
resolution_items[item_text] = item
cls.resolution_items = resolution_items
return cls.resolution_items
@classmethod
def get_attr_defs_for_instance(
cls, create_context, instance
):
if instance.product_type not in cls.product_types:
return []
# Get the resolution items
resolution_items = cls._get_resolution_items()
items = [cls.default_resolution_item]
# Add all cached resolution items to the dropdown options
for item_text in resolution_items:
items.append((item_text, item_text))
return [
EnumDef(
"explicit_resolution",
items,
default="Don't override",
label="Override Resolution"
)
]

View file

@ -1,4 +1,6 @@
from collections.abc import Iterable
from pydantic import validator
from typing import Any
from ayon_server.settings import (
BaseSettingsModel,
@ -8,7 +10,7 @@ from ayon_server.settings import (
ensure_unique_names,
task_types_enum,
)
from ayon_server.exceptions import BadRequestException
from ayon_server.types import ColorRGBA_uint8
@ -157,6 +159,77 @@ class CollectUSDLayerContributionsModel(BaseSettingsModel):
return value
class ResolutionOptionsModel(BaseSettingsModel):
_layout = "compact"
width: int = SettingsField(
1920,
ge=0,
le=100000,
title="Width",
description=(
"Width resolution number value"),
placeholder="Width"
)
height: int = SettingsField(
1080,
title="Height",
ge=0,
le=100000,
description=(
"Height resolution number value"),
placeholder="Height"
)
pixel_aspect: float = SettingsField(
1.0,
title="Pixel aspect",
ge=0.0,
le=100000.0,
description=(
"Pixel Aspect resolution decimal number value"),
placeholder="Pixel aspect"
)
def ensure_unique_resolution_option(
objects: Iterable[Any], field_name: str | None = None) -> None: # noqa: C901
"""Ensure a list of objects have unique option attributes.
This function checks if the list of objects has unique 'width',
'height' and 'pixel_aspect' properties.
"""
options = []
for obj in objects:
item_test_text = f"{obj.width}x{obj.height}x{obj.pixel_aspect}"
if item_test_text not in options:
options.append(item_test_text)
else:
raise BadRequestException(
f"Duplicate option '{item_test_text}'")
class CollectExplicitResolutionModel(BaseSettingsModel):
enabled: bool = SettingsField(True, title="Enabled")
product_types: list[str] = SettingsField(
default_factory=list,
title="Product types",
description=(
"Only activate the attribute for following product types."
)
)
options: list[ResolutionOptionsModel] = SettingsField(
default_factory=list,
title="Resolution options",
description=(
"Options to be provided in publisher attribute"
)
)
@validator("options")
def validate_unique_options(cls, value):
ensure_unique_resolution_option(value)
return value
class AyonEntityURIModel(BaseSettingsModel):
use_ayon_entity_uri: bool = SettingsField(
title="Use AYON Entity URI",
@ -988,6 +1061,10 @@ class PublishPuginsModel(BaseSettingsModel):
title="Collect USD Layer Contributions",
)
)
CollectExplicitResolution: CollectExplicitResolutionModel = SettingsField(
default_factory=CollectExplicitResolutionModel,
title="Collect Explicit Resolution"
)
ValidateEditorialAssetName: ValidateBaseModel = SettingsField(
default_factory=ValidateBaseModel,
title="Validate Editorial Asset Name"
@ -1162,6 +1239,19 @@ DEFAULT_PUBLISH_VALUES = {
},
]
},
"CollectExplicitResolution": {
"enabled": True,
"product_types": [
"shot"
],
"options": [
{
"width": 2048,
"height": 1080,
"aspect_ratio": 1.5,
}
]
},
"ValidateEditorialAssetName": {
"enabled": True,
"optional": False,