mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge remote-tracking branch 'origin/develop' into enhancement/1296-product-base-types-support-in-integrator
This commit is contained in:
commit
85668a1b74
16 changed files with 223 additions and 82 deletions
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1232,6 +1232,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
|
||||
|
|
@ -1241,22 +1249,25 @@ 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
|
||||
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
|
||||
|
|
@ -1281,24 +1292,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,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import platform
|
|||
import tempfile
|
||||
import warnings
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass
|
||||
|
||||
import ayon_api
|
||||
|
||||
|
|
@ -26,6 +27,18 @@ 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 = {}
|
||||
has_compatible_ocio_package = None
|
||||
|
|
@ -710,7 +723,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 +732,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 +748,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 +772,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 +791,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 +812,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 +833,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 +869,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 +882,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,13 +1029,19 @@ 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"
|
||||
)
|
||||
|
||||
return config_data
|
||||
return {
|
||||
"path": config_data.path,
|
||||
"template": config_data.template,
|
||||
}
|
||||
|
||||
|
||||
def _get_host_config_data(templates, template_data):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
||||
|
|
@ -589,7 +602,6 @@ def create_instances_for_aov(
|
|||
"""
|
||||
# we cannot attach AOVs to other products as we consider every
|
||||
# AOV product of its own.
|
||||
|
||||
log = Logger.get_logger("farm_publishing")
|
||||
|
||||
# if there are product to attach to and more than one AOV,
|
||||
|
|
@ -612,8 +624,8 @@ def create_instances_for_aov(
|
|||
additional_data.update({
|
||||
"colorspaceConfig": colorspace_config,
|
||||
# Display/View are optional
|
||||
"display": instance.data.get("colorspaceDisplay"),
|
||||
"view": instance.data.get("colorspaceView")
|
||||
"display": instance.data.get("sourceDisplay"),
|
||||
"view": instance.data.get("sourceView")
|
||||
})
|
||||
|
||||
# Get templated path from absolute config path.
|
||||
|
|
|
|||
|
|
@ -816,7 +816,22 @@ 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_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["hostName"],
|
||||
product_type=workfile_instance.data["productType"],
|
||||
task_name=task_name,
|
||||
task_type=task_type,
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
"flame",
|
||||
"unreal",
|
||||
"batchdelivery",
|
||||
"photoshop"
|
||||
"photoshop",
|
||||
"substancepainter",
|
||||
]
|
||||
|
||||
settings_category = "core"
|
||||
|
|
|
|||
|
|
@ -500,7 +500,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(
|
||||
|
|
@ -844,6 +844,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=asset_name, init_type=init_type
|
||||
)
|
||||
|
|
@ -925,7 +926,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":
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring AYON addon 'core' version."""
|
||||
__version__ = "1.6.11+dev"
|
||||
__version__ = "1.6.12+dev"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name = "core"
|
||||
title = "Core"
|
||||
version = "1.6.11+dev"
|
||||
version = "1.6.12+dev"
|
||||
|
||||
client_dir = "ayon_core"
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
[tool.poetry]
|
||||
name = "ayon-core"
|
||||
version = "1.6.11+dev"
|
||||
version = "1.6.12+dev"
|
||||
description = ""
|
||||
authors = ["Ynput Team <team@ynput.io>"]
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -456,7 +456,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(
|
||||
|
|
@ -464,7 +464,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."
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -1584,6 +1584,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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue