Merge branch 'develop' into bugfix/fps_retime_rounding_issue

This commit is contained in:
robin@ynput.io 2025-01-10 12:55:02 +01:00
commit 0b2580bc63
6 changed files with 165 additions and 71 deletions

View file

@ -15,7 +15,7 @@ from ayon_core.pipeline.plugin_discover import (
deregister_plugin, deregister_plugin,
deregister_plugin_path deregister_plugin_path
) )
from ayon_core.pipeline import get_staging_dir_info from ayon_core.pipeline.staging_dir import get_staging_dir_info, StagingDir
from .constants import DEFAULT_VARIANT_VALUE from .constants import DEFAULT_VARIANT_VALUE
from .product_name import get_product_name from .product_name import get_product_name
@ -833,7 +833,7 @@ class Creator(BaseCreator):
""" """
return self.pre_create_attr_defs return self.pre_create_attr_defs
def get_staging_dir(self, instance): def get_staging_dir(self, instance) -> Optional[StagingDir]:
"""Return the staging dir and persistence from instance. """Return the staging dir and persistence from instance.
Args: Args:
@ -915,7 +915,7 @@ class Creator(BaseCreator):
instance.transient_data.update({ instance.transient_data.update({
"stagingDir": staging_dir_path, "stagingDir": staging_dir_path,
"stagingDir_persistent": staging_dir_info.persistent, "stagingDir_persistent": staging_dir_info.is_persistent,
}) })
self.log.info(f"Applied staging dir to instance: {staging_dir_path}") self.log.info(f"Applied staging dir to instance: {staging_dir_path}")

View file

@ -238,10 +238,13 @@ def remap_range_on_file_sequence(otio_clip, otio_range):
# source range for image sequence. Following code maintain # source range for image sequence. Following code maintain
# backward-compatibility by adjusting media_in # backward-compatibility by adjusting media_in
# while we are updating those. # while we are updating those.
conformed_src_in = source_range.start_time.rescaled_to(
available_range_rate
)
if ( if (
is_clip_from_media_sequence(otio_clip) is_clip_from_media_sequence(otio_clip)
and available_range_start_frame == media_ref.start_frame and available_range_start_frame == media_ref.start_frame
and source_range.start_time.to_frames() < media_ref.start_frame and conformed_src_in.to_frames() < media_ref.start_frame
): ):
media_in = otio.opentime.RationalTime( media_in = otio.opentime.RationalTime(
0, rate=available_range_rate 0, rate=available_range_rate
@ -272,21 +275,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
media_ref = otio_clip.media_reference media_ref = otio_clip.media_reference
is_input_sequence = is_clip_from_media_sequence(otio_clip) is_input_sequence = is_clip_from_media_sequence(otio_clip)
# Temporary.
# Some AYON custom OTIO exporter were implemented with relative
# source range for image sequence. Following code maintain
# backward-compatibility by adjusting available range
# while we are updating those.
if (
is_input_sequence
and available_range.start_time.to_frames() == media_ref.start_frame
and source_range.start_time.to_frames() < media_ref.start_frame
):
available_range = _ot.TimeRange(
_ot.RationalTime(0, rate=available_range_rate),
available_range.duration,
)
# Conform source range bounds to available range rate # Conform source range bounds to available range rate
# .e.g. embedded TC of (3600 sec/ 1h), duration 100 frames # .e.g. embedded TC of (3600 sec/ 1h), duration 100 frames
# #
@ -331,6 +319,22 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
else: else:
conformed_source_range = source_range conformed_source_range = source_range
# Temporary.
# Some AYON custom OTIO exporter were implemented with relative
# source range for image sequence. Following code maintain
# backward-compatibility by adjusting available range
# while we are updating those.
if (
is_input_sequence
and available_range.start_time.to_frames() == media_ref.start_frame
and conformed_source_range.start_time.to_frames() <
media_ref.start_frame
):
available_range = _ot.TimeRange(
_ot.RationalTime(0, rate=available_range_rate),
available_range.duration,
)
# modifiers # modifiers
time_scalar = 1. time_scalar = 1.
offset_in = 0 offset_in = 0

View file

@ -716,8 +716,8 @@ def get_instance_staging_dir(instance):
os.makedirs(staging_dir_path, exist_ok=True) os.makedirs(staging_dir_path, exist_ok=True)
instance.data.update({ instance.data.update({
"stagingDir": staging_dir_path, "stagingDir": staging_dir_path,
"stagingDir_persistent": staging_dir_info.persistent, "stagingDir_persistent": staging_dir_info.is_persistent,
"stagingDir_custom": staging_dir_info.custom "stagingDir_is_custom": staging_dir_info.is_custom
}) })
return staging_dir_path return staging_dir_path

View file

@ -1,3 +1,6 @@
import logging
import warnings
from typing import Optional, Dict, Any
from dataclasses import dataclass from dataclasses import dataclass
from ayon_core.lib import Logger, filter_profiles from ayon_core.lib import Logger, filter_profiles
@ -11,21 +14,41 @@ from .tempdir import get_temp_dir
@dataclass @dataclass
class StagingDir: class StagingDir:
directory: str directory: str
persistent: bool is_persistent: bool
custom: bool # Whether the staging dir is a custom staging dir # Whether the staging dir is a custom staging dir
is_custom: bool
def __setattr__(self, key, value):
if key == "persistent":
warnings.warn(
"'StagingDir.persistent' is deprecated."
" Use 'StagingDir.is_persistent' instead.",
DeprecationWarning
)
key = "is_persistent"
super().__setattr__(key, value)
@property
def persistent(self):
warnings.warn(
"'StagingDir.persistent' is deprecated."
" Use 'StagingDir.is_persistent' instead.",
DeprecationWarning
)
return self.is_persistent
def get_staging_dir_config( def get_staging_dir_config(
project_name, project_name: str,
task_type, task_type: Optional[str],
task_name, task_name: Optional[str],
product_type, product_type: str,
product_name, product_name: str,
host_name, host_name: str,
project_settings=None, project_settings: Optional[Dict[str, Any]] = None,
anatomy=None, anatomy: Optional[Anatomy] = None,
log=None, log: Optional[logging.Logger] = None,
): ) -> Optional[Dict[str, Any]]:
"""Get matching staging dir profile. """Get matching staging dir profile.
Args: Args:
@ -76,7 +99,6 @@ def get_staging_dir_config(
# get template from template name # get template from template name
template_name = profile["template_name"] template_name = profile["template_name"]
_validate_template_name(project_name, template_name, anatomy)
template = anatomy.get_template_item("staging", template_name) template = anatomy.get_template_item("staging", template_name)
@ -93,35 +115,22 @@ def get_staging_dir_config(
return {"template": template, "persistence": data_persistence} return {"template": template, "persistence": data_persistence}
def _validate_template_name(project_name, template_name, anatomy):
"""Check that staging dir section with appropriate template exist.
Raises:
ValueError - if misconfigured template
"""
if template_name not in anatomy.templates["staging"]:
raise ValueError(
f'Anatomy of project "{project_name}" does not have set'
f' "{template_name}" template key at Staging Dir category!'
)
def get_staging_dir_info( def get_staging_dir_info(
project_entity, project_entity: Dict[str, Any],
folder_entity, folder_entity: Optional[Dict[str, Any]],
task_entity, task_entity: Optional[Dict[str, Any]],
product_type, product_type: str,
product_name, product_name: str,
host_name, host_name: str,
anatomy=None, anatomy: Optional[Anatomy] = None,
project_settings=None, project_settings: Optional[Dict[str, Any]] = None,
template_data=None, template_data: Optional[Dict[str, Any]] = None,
always_return_path=True, always_return_path: bool = True,
force_tmp_dir=False, force_tmp_dir: bool = False,
logger=None, logger: Optional[logging.Logger] = None,
prefix=None, prefix: Optional[str] = None,
suffix=None, suffix: Optional[str] = None,
): ) -> Optional[StagingDir]:
"""Get staging dir info data. """Get staging dir info data.
If `force_temp` is set, staging dir will be created as tempdir. If `force_temp` is set, staging dir will be created as tempdir.
@ -161,11 +170,15 @@ def get_staging_dir_info(
) )
if force_tmp_dir: if force_tmp_dir:
return get_temp_dir( return StagingDir(
project_name=project_entity["name"], get_temp_dir(
anatomy=anatomy, project_name=project_entity["name"],
prefix=prefix, anatomy=anatomy,
suffix=suffix, prefix=prefix,
suffix=suffix,
),
is_persistent=False,
is_custom=False
) )
# making few queries to database # making few queries to database
@ -205,8 +218,8 @@ def get_staging_dir_info(
dir_template = staging_dir_config["template"]["directory"] dir_template = staging_dir_config["template"]["directory"]
return StagingDir( return StagingDir(
dir_template.format_strict(ctx_data), dir_template.format_strict(ctx_data),
persistent=staging_dir_config["persistence"], is_persistent=staging_dir_config["persistence"],
custom=True is_custom=True
) )
# no config found but force an output # no config found but force an output
@ -218,8 +231,8 @@ def get_staging_dir_info(
prefix=prefix, prefix=prefix,
suffix=suffix, suffix=suffix,
), ),
persistent=False, is_persistent=False,
custom=False is_custom=False
) )
return None return None

View file

@ -0,0 +1,51 @@
{
"OTIO_SCHEMA": "Clip.2",
"metadata": {},
"name": "",
"source_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 23.976,
"value": 108.0
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 23.976,
"value": 883159.0
}
},
"effects": [],
"markers": [],
"enabled": true,
"media_references": {
"DEFAULT_MEDIA": {
"OTIO_SCHEMA": "ImageSequenceReference.1",
"metadata": {},
"name": "",
"available_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24.0,
"value": 755.0
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24.0,
"value": 883750.0
}
},
"available_image_bounds": null,
"target_url_base": "/mnt/jobs/yahoo_theDog_1132/IN/FOOTAGE/SCANS_LINEAR/Panasonic Rec 709 to ACESCG/Panasonic P2 /A001_S001_S001_T004/",
"name_prefix": "A001_S001_S001_T004.",
"name_suffix": ".exr",
"start_frame": 883750,
"frame_step": 1,
"rate": 1.0,
"frame_zero_padding": 0,
"missing_frame_policy": "error"
}
},
"active_media_reference_key": "DEFAULT_MEDIA"
}

View file

@ -209,3 +209,29 @@ def test_img_sequence_conform_to_23_976fps():
handle_start=0, handle_start=0,
handle_end=8, handle_end=8,
) )
def test_img_sequence_conform_from_24_to_23_976fps():
"""
Img sequence clip
available files = 883750-884504 24fps
source_range = 883159-883267 23.976fps
This test ensures such entries do not trigger
the legacy Hiero export compatibility.
"""
expected_data = {
'mediaIn': 884043,
'mediaOut': 884150,
'handleStart': 0,
'handleEnd': 0,
'speed': 1.0
}
_check_expected_retimed_values(
"img_seq_24_to_23.976_no_legacy.json",
expected_data,
handle_start=0,
handle_end=0,
)