mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into enhancement/AY-2420_Callbacks-and-groups-with-Publisher-attributes
# Conflicts: # client/ayon_core/tools/publisher/widgets/widgets.py
This commit is contained in:
commit
165f6a6626
13 changed files with 446 additions and 55 deletions
|
|
@ -28,7 +28,8 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook):
|
|||
"substancepainter",
|
||||
"aftereffects",
|
||||
"wrap",
|
||||
"openrv"
|
||||
"openrv",
|
||||
"cinema4d"
|
||||
}
|
||||
launch_types = {LaunchTypes.local}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import collections
|
|||
import uuid
|
||||
import json
|
||||
import copy
|
||||
import warnings
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import clique
|
||||
|
|
@ -90,6 +91,30 @@ class AbstractAttrDefMeta(ABCMeta):
|
|||
return obj
|
||||
|
||||
|
||||
def _convert_reversed_attr(
|
||||
main_value, depr_value, main_label, depr_label, default
|
||||
):
|
||||
if main_value is not None and depr_value is not None:
|
||||
if main_value == depr_value:
|
||||
print(
|
||||
f"Got invalid '{main_label}' and '{depr_label}' arguments."
|
||||
f" Using '{main_label}' value."
|
||||
)
|
||||
elif depr_value is not None:
|
||||
warnings.warn(
|
||||
(
|
||||
"DEPRECATION WARNING: Using deprecated argument"
|
||||
f" '{depr_label}' please use '{main_label}' instead."
|
||||
),
|
||||
DeprecationWarning,
|
||||
stacklevel=4,
|
||||
)
|
||||
main_value = not depr_value
|
||||
elif main_value is None:
|
||||
main_value = default
|
||||
return main_value
|
||||
|
||||
|
||||
class AbstractAttrDef(metaclass=AbstractAttrDefMeta):
|
||||
"""Abstraction of attribute definition.
|
||||
|
||||
|
|
@ -106,12 +131,14 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta):
|
|||
Args:
|
||||
key (str): Under which key will be attribute value stored.
|
||||
default (Any): Default value of an attribute.
|
||||
label (str): Attribute label.
|
||||
tooltip (str): Attribute tooltip.
|
||||
is_label_horizontal (bool): UI specific argument. Specify if label is
|
||||
next to value input or ahead.
|
||||
hidden (bool): Will be item hidden (for UI purposes).
|
||||
disabled (bool): Item will be visible but disabled (for UI purposes).
|
||||
label (Optional[str]): Attribute label.
|
||||
tooltip (Optional[str]): Attribute tooltip.
|
||||
is_label_horizontal (Optional[bool]): UI specific argument. Specify
|
||||
if label is next to value input or ahead.
|
||||
visible (Optional[bool]): Item is shown to user (for UI purposes).
|
||||
enabled (Optional[bool]): Item is enabled (for UI purposes).
|
||||
hidden (Optional[bool]): DEPRECATED: Use 'visible' instead.
|
||||
disabled (Optional[bool]): DEPRECATED: Use 'enabled' instead.
|
||||
"""
|
||||
|
||||
type_attributes = []
|
||||
|
|
@ -125,22 +152,28 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta):
|
|||
label=None,
|
||||
tooltip=None,
|
||||
is_label_horizontal=None,
|
||||
hidden=False,
|
||||
disabled=False
|
||||
visible=None,
|
||||
enabled=None,
|
||||
hidden=None,
|
||||
disabled=None,
|
||||
):
|
||||
if is_label_horizontal is None:
|
||||
is_label_horizontal = True
|
||||
|
||||
if hidden is None:
|
||||
hidden = False
|
||||
enabled = _convert_reversed_attr(
|
||||
enabled, disabled, "enabled", "disabled", True
|
||||
)
|
||||
visible = _convert_reversed_attr(
|
||||
visible, hidden, "visible", "hidden", True
|
||||
)
|
||||
|
||||
self.key = key
|
||||
self.label = label
|
||||
self.tooltip = tooltip
|
||||
self.default = default
|
||||
self.is_label_horizontal = is_label_horizontal
|
||||
self.hidden = hidden
|
||||
self.disabled = disabled
|
||||
self.visible = visible
|
||||
self.enabled = enabled
|
||||
self._id = uuid.uuid4().hex
|
||||
|
||||
self.__init__class__ = AbstractAttrDef
|
||||
|
|
@ -149,14 +182,30 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta):
|
|||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def hidden(self):
|
||||
return not self.visible
|
||||
|
||||
@hidden.setter
|
||||
def hidden(self, value):
|
||||
self.visible = not value
|
||||
|
||||
@property
|
||||
def disabled(self):
|
||||
return not self.enabled
|
||||
|
||||
@disabled.setter
|
||||
def disabled(self, value):
|
||||
self.enabled = not value
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return (
|
||||
self.key == other.key
|
||||
and self.hidden == other.hidden
|
||||
and self.default == other.default
|
||||
and self.disabled == other.disabled
|
||||
and self.visible == other.visible
|
||||
and self.enabled == other.enabled
|
||||
)
|
||||
|
||||
def __ne__(self, other):
|
||||
|
|
@ -198,8 +247,8 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta):
|
|||
"tooltip": self.tooltip,
|
||||
"default": self.default,
|
||||
"is_label_horizontal": self.is_label_horizontal,
|
||||
"hidden": self.hidden,
|
||||
"disabled": self.disabled
|
||||
"visible": self.visible,
|
||||
"enabled": self.enabled
|
||||
}
|
||||
for attr in self.type_attributes:
|
||||
data[attr] = getattr(self, attr)
|
||||
|
|
@ -279,8 +328,8 @@ class HiddenDef(AbstractAttrDef):
|
|||
|
||||
def __init__(self, key, default=None, **kwargs):
|
||||
kwargs["default"] = default
|
||||
kwargs["hidden"] = True
|
||||
super(HiddenDef, self).__init__(key, **kwargs)
|
||||
kwargs["visible"] = False
|
||||
super().__init__(key, **kwargs)
|
||||
|
||||
def convert_value(self, value):
|
||||
return value
|
||||
|
|
|
|||
|
|
@ -292,13 +292,26 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
|
|||
# Note that 24fps is slower than 25fps hence extended duration
|
||||
# to preserve media range
|
||||
|
||||
# Compute new source range based on available rate
|
||||
conformed_src_in = source_range.start_time.rescaled_to(available_range_rate)
|
||||
conformed_src_duration = source_range.duration.rescaled_to(available_range_rate)
|
||||
conformed_source_range = otio.opentime.TimeRange(
|
||||
start_time=conformed_src_in,
|
||||
duration=conformed_src_duration
|
||||
)
|
||||
# Compute new source range based on available rate.
|
||||
|
||||
# Backward-compatibility for Hiero OTIO exporter.
|
||||
# NTSC compatibility might introduce floating rates, when these are
|
||||
# not exactly the same (23.976 vs 23.976024627685547)
|
||||
# this will cause precision issue in computation.
|
||||
# Currently round to 2 decimals for comparison,
|
||||
# but this should always rescale after that.
|
||||
rounded_av_rate = round(available_range_rate, 2)
|
||||
rounded_src_rate = round(source_range.start_time.rate, 2)
|
||||
if rounded_av_rate != rounded_src_rate:
|
||||
conformed_src_in = source_range.start_time.rescaled_to(available_range_rate)
|
||||
conformed_src_duration = source_range.duration.rescaled_to(available_range_rate)
|
||||
conformed_source_range = otio.opentime.TimeRange(
|
||||
start_time=conformed_src_in,
|
||||
duration=conformed_src_duration
|
||||
)
|
||||
|
||||
else:
|
||||
conformed_source_range = source_range
|
||||
|
||||
# modifiers
|
||||
time_scalar = 1.
|
||||
|
|
|
|||
|
|
@ -242,6 +242,26 @@ class LoaderPlugin(list):
|
|||
if hasattr(self, "_fname"):
|
||||
return self._fname
|
||||
|
||||
@classmethod
|
||||
def get_representation_name_aliases(cls, representation_name: str):
|
||||
"""Return representation names to which switching is allowed from
|
||||
the input representation name, like an alias replacement of the input
|
||||
`representation_name`.
|
||||
|
||||
For example, to allow an automated switch on update from representation
|
||||
`ma` to `mb` or `abc`, then when `representation_name` is `ma` return:
|
||||
["mb", "abc"]
|
||||
|
||||
The order of the names in the returned representation names is
|
||||
important, because the first one existing under the new version will
|
||||
be chosen.
|
||||
|
||||
Returns:
|
||||
List[str]: Representation names switching to is allowed on update
|
||||
if the input representation name is not found on the new version.
|
||||
"""
|
||||
return []
|
||||
|
||||
|
||||
class ProductLoaderPlugin(LoaderPlugin):
|
||||
"""Load product into host application
|
||||
|
|
|
|||
|
|
@ -505,21 +505,6 @@ def update_container(container, version=-1):
|
|||
project_name, product_entity["folderId"]
|
||||
)
|
||||
|
||||
repre_name = current_representation["name"]
|
||||
new_representation = ayon_api.get_representation_by_name(
|
||||
project_name, repre_name, new_version["id"]
|
||||
)
|
||||
if new_representation is None:
|
||||
raise ValueError(
|
||||
"Representation '{}' wasn't found on requested version".format(
|
||||
repre_name
|
||||
)
|
||||
)
|
||||
|
||||
path = get_representation_path(new_representation)
|
||||
if not path or not os.path.exists(path):
|
||||
raise ValueError("Path {} doesn't exist".format(path))
|
||||
|
||||
# Run update on the Loader for this container
|
||||
Loader = _get_container_loader(container)
|
||||
if not Loader:
|
||||
|
|
@ -527,6 +512,39 @@ def update_container(container, version=-1):
|
|||
"Can't update container because loader '{}' was not found."
|
||||
.format(container.get("loader"))
|
||||
)
|
||||
|
||||
repre_name = current_representation["name"]
|
||||
new_representation = ayon_api.get_representation_by_name(
|
||||
project_name, repre_name, new_version["id"]
|
||||
)
|
||||
if new_representation is None:
|
||||
# The representation name is not found in the new version.
|
||||
# Allow updating to a 'matching' representation if the loader
|
||||
# has defined compatible update conversions
|
||||
repre_name_aliases = Loader.get_representation_name_aliases(repre_name)
|
||||
if repre_name_aliases:
|
||||
representations = ayon_api.get_representations(
|
||||
project_name,
|
||||
representation_names=repre_name_aliases,
|
||||
version_ids=[new_version["id"]])
|
||||
representations_by_name = {
|
||||
repre["name"]: repre for repre in representations
|
||||
}
|
||||
for name in repre_name_aliases:
|
||||
if name in representations_by_name:
|
||||
new_representation = representations_by_name[name]
|
||||
break
|
||||
|
||||
if new_representation is None:
|
||||
raise ValueError(
|
||||
"Representation '{}' wasn't found on requested version".format(
|
||||
repre_name
|
||||
)
|
||||
)
|
||||
|
||||
path = get_representation_path(new_representation)
|
||||
if not path or not os.path.exists(path):
|
||||
raise ValueError("Path {} doesn't exist".format(path))
|
||||
project_entity = ayon_api.get_project(project_name)
|
||||
context = {
|
||||
"project": project_entity,
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ from .files_widget import FilesWidget
|
|||
|
||||
def create_widget_for_attr_def(attr_def, parent=None):
|
||||
widget = _create_widget_for_attr_def(attr_def, parent)
|
||||
if attr_def.hidden:
|
||||
if not attr_def.visible:
|
||||
widget.setVisible(False)
|
||||
|
||||
if attr_def.disabled:
|
||||
if not attr_def.enabled:
|
||||
widget.setEnabled(False)
|
||||
return widget
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget):
|
|||
widget = create_widget_for_attr_def(attr_def, self)
|
||||
self._widgets.append(widget)
|
||||
|
||||
if attr_def.hidden:
|
||||
if not attr_def.visible:
|
||||
continue
|
||||
|
||||
expand_cols = 2
|
||||
|
|
|
|||
|
|
@ -32,17 +32,20 @@ PLUGIN_ORDER_OFFSET = 0.5
|
|||
class MessageHandler(logging.Handler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.records = []
|
||||
self._records = []
|
||||
|
||||
def clear_records(self):
|
||||
self.records = []
|
||||
self._records = []
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
record.msg = record.getMessage()
|
||||
except Exception:
|
||||
record.msg = str(record.msg)
|
||||
self.records.append(record)
|
||||
self._records.append(record)
|
||||
|
||||
def get_records(self):
|
||||
return self._records
|
||||
|
||||
|
||||
class PublishErrorInfo:
|
||||
|
|
@ -1328,7 +1331,18 @@ class PublishModel:
|
|||
plugin, self._publish_context, instance
|
||||
)
|
||||
if log_handler is not None:
|
||||
result["records"] = log_handler.records
|
||||
records = log_handler.get_records()
|
||||
exception = result.get("error")
|
||||
if exception is not None and records:
|
||||
last_record = records[-1]
|
||||
if (
|
||||
last_record.name == "pyblish.plugin"
|
||||
and last_record.levelno == logging.ERROR
|
||||
):
|
||||
# Remove last record made by pyblish
|
||||
# - `log.exception(formatted_traceback)`
|
||||
records.pop(-1)
|
||||
result["records"] = records
|
||||
|
||||
exception = result.get("error")
|
||||
if exception:
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
|
|||
self._attr_def_id_to_instances[attr_def.id] = instance_ids
|
||||
self._attr_def_id_to_attr_def[attr_def.id] = attr_def
|
||||
|
||||
if attr_def.hidden:
|
||||
if not attr_def.visible:
|
||||
continue
|
||||
|
||||
expand_cols = 2
|
||||
|
|
@ -282,15 +282,15 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
|
|||
widget = create_widget_for_attr_def(
|
||||
attr_def, content_widget
|
||||
)
|
||||
hidden_widget = attr_def.hidden
|
||||
visible_widget = attr_def.visible
|
||||
# Hide unknown values of publish plugins
|
||||
# - The keys in most of the cases does not represent what
|
||||
# would label represent
|
||||
if isinstance(attr_def, UnknownDef):
|
||||
widget.setVisible(False)
|
||||
hidden_widget = True
|
||||
visible_widget = False
|
||||
|
||||
if not hidden_widget:
|
||||
if visible_widget:
|
||||
expand_cols = 2
|
||||
if attr_def.is_value_def and attr_def.is_label_horizontal:
|
||||
expand_cols = 1
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring AYON addon 'core' version."""
|
||||
__version__ = "1.0.0+dev"
|
||||
__version__ = "1.0.1+dev"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name = "core"
|
||||
title = "Core"
|
||||
version = "1.0.0+dev"
|
||||
version = "1.0.1+dev"
|
||||
|
||||
client_dir = "ayon_core"
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
[tool.poetry]
|
||||
name = "ayon-core"
|
||||
version = "1.0.0+dev"
|
||||
version = "1.0.1+dev"
|
||||
description = ""
|
||||
authors = ["Ynput Team <team@ynput.io>"]
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,255 @@
|
|||
{
|
||||
"OTIO_SCHEMA": "Clip.2",
|
||||
"metadata": {
|
||||
"active": true,
|
||||
"applieswhole": 1,
|
||||
"asset": "sh020",
|
||||
"audio": true,
|
||||
"families": [
|
||||
"clip"
|
||||
],
|
||||
"family": "plate",
|
||||
"handleEnd": 8,
|
||||
"handleStart": 0,
|
||||
"heroTrack": true,
|
||||
"hierarchy": "shots/sq001",
|
||||
"hierarchyData": {
|
||||
"episode": "ep01",
|
||||
"folder": "shots",
|
||||
"sequence": "sq001",
|
||||
"shot": "sh020",
|
||||
"track": "reference"
|
||||
},
|
||||
"hiero_source_type": "TrackItem",
|
||||
"id": "pyblish.avalon.instance",
|
||||
"label": "openpypeData",
|
||||
"note": "OpenPype data container",
|
||||
"parents": [
|
||||
{
|
||||
"entity_name": "shots",
|
||||
"entity_type": "folder"
|
||||
},
|
||||
{
|
||||
"entity_name": "sq001",
|
||||
"entity_type": "sequence"
|
||||
}
|
||||
],
|
||||
"publish": true,
|
||||
"reviewTrack": null,
|
||||
"sourceResolution": false,
|
||||
"subset": "plateP01",
|
||||
"variant": "Main",
|
||||
"workfileFrameStart": 1001
|
||||
},
|
||||
"name": "sh020",
|
||||
"source_range": {
|
||||
"OTIO_SCHEMA": "TimeRange.1",
|
||||
"duration": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976024627685547,
|
||||
"value": 51.0
|
||||
},
|
||||
"start_time": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976024627685547,
|
||||
"value": 0.0
|
||||
}
|
||||
},
|
||||
"effects": [],
|
||||
"markers": [
|
||||
{
|
||||
"OTIO_SCHEMA": "Marker.2",
|
||||
"metadata": {
|
||||
"active": true,
|
||||
"applieswhole": 1,
|
||||
"asset": "sh020",
|
||||
"audio": true,
|
||||
"families": [
|
||||
"clip"
|
||||
],
|
||||
"family": "plate",
|
||||
"handleEnd": 8,
|
||||
"handleStart": 0,
|
||||
"heroTrack": true,
|
||||
"hierarchy": "shots/sq001",
|
||||
"hierarchyData": {
|
||||
"episode": "ep01",
|
||||
"folder": "shots",
|
||||
"sequence": "sq001",
|
||||
"shot": "sh020",
|
||||
"track": "reference"
|
||||
},
|
||||
"hiero_source_type": "TrackItem",
|
||||
"id": "pyblish.avalon.instance",
|
||||
"label": "openpypeData",
|
||||
"note": "OpenPype data container",
|
||||
"parents": [
|
||||
{
|
||||
"entity_name": "shots",
|
||||
"entity_type": "folder"
|
||||
},
|
||||
{
|
||||
"entity_name": "sq001",
|
||||
"entity_type": "sequence"
|
||||
}
|
||||
],
|
||||
"publish": true,
|
||||
"reviewTrack": null,
|
||||
"sourceResolution": false,
|
||||
"subset": "plateP01",
|
||||
"variant": "Main",
|
||||
"workfileFrameStart": 1001
|
||||
},
|
||||
"name": "openpypeData",
|
||||
"color": "RED",
|
||||
"marked_range": {
|
||||
"OTIO_SCHEMA": "TimeRange.1",
|
||||
"duration": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976024627685547,
|
||||
"value": 0.0
|
||||
},
|
||||
"start_time": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976024627685547,
|
||||
"value": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"OTIO_SCHEMA": "Marker.2",
|
||||
"metadata": {
|
||||
"applieswhole": 1,
|
||||
"family": "task",
|
||||
"hiero_source_type": "TrackItem",
|
||||
"label": "comp",
|
||||
"note": "Compositing",
|
||||
"type": "Compositing"
|
||||
},
|
||||
"name": "comp",
|
||||
"color": "RED",
|
||||
"marked_range": {
|
||||
"OTIO_SCHEMA": "TimeRange.1",
|
||||
"duration": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976024627685547,
|
||||
"value": 0.0
|
||||
},
|
||||
"start_time": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976024627685547,
|
||||
"value": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"enabled": true,
|
||||
"media_references": {
|
||||
"DEFAULT_MEDIA": {
|
||||
"OTIO_SCHEMA": "ImageSequenceReference.1",
|
||||
"metadata": {
|
||||
"clip.properties.blendfunc": "0",
|
||||
"clip.properties.colourspacename": "default",
|
||||
"clip.properties.domainroot": "",
|
||||
"clip.properties.enabled": "1",
|
||||
"clip.properties.expanded": "1",
|
||||
"clip.properties.opacity": "1",
|
||||
"clip.properties.valuesource": "",
|
||||
"foundry.source.audio": "",
|
||||
"foundry.source.bitmapsize": "0",
|
||||
"foundry.source.bitsperchannel": "0",
|
||||
"foundry.source.channelformat": "integer",
|
||||
"foundry.source.colourtransform": "ACES - ACES2065-1",
|
||||
"foundry.source.duration": "59",
|
||||
"foundry.source.filename": "MER_sq001_sh020_P01.%04d.exr 997-1055",
|
||||
"foundry.source.filesize": "",
|
||||
"foundry.source.fragments": "59",
|
||||
"foundry.source.framerate": "23.98",
|
||||
"foundry.source.fullpath": "",
|
||||
"foundry.source.height": "1080",
|
||||
"foundry.source.layers": "colour",
|
||||
"foundry.source.path": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P01/MER_sq001_sh020_P01.%04d.exr 997-1055",
|
||||
"foundry.source.pixelAspect": "1",
|
||||
"foundry.source.pixelAspectRatio": "",
|
||||
"foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11",
|
||||
"foundry.source.reelID": "",
|
||||
"foundry.source.resolution": "",
|
||||
"foundry.source.samplerate": "Invalid",
|
||||
"foundry.source.shortfilename": "MER_sq001_sh020_P01.%04d.exr 997-1055",
|
||||
"foundry.source.shot": "",
|
||||
"foundry.source.shotDate": "",
|
||||
"foundry.source.startTC": "",
|
||||
"foundry.source.starttime": "997",
|
||||
"foundry.source.timecode": "172800",
|
||||
"foundry.source.umid": "1bf7437a-b446-440c-07c5-7cae7acf4f5e",
|
||||
"foundry.source.umidOriginator": "foundry.source.umid",
|
||||
"foundry.source.width": "1920",
|
||||
"foundry.timeline.autodiskcachemode": "Manual",
|
||||
"foundry.timeline.colorSpace": "ACES - ACES2065-1",
|
||||
"foundry.timeline.duration": "59",
|
||||
"foundry.timeline.framerate": "23.98",
|
||||
"foundry.timeline.outputformat": "",
|
||||
"foundry.timeline.poster": "0",
|
||||
"foundry.timeline.posterLayer": "colour",
|
||||
"foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAMAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=",
|
||||
"foundry.timeline.samplerate": "Invalid",
|
||||
"isSequence": true,
|
||||
"media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}",
|
||||
"media.exr.compression": "8",
|
||||
"media.exr.compressionName": "DWAA",
|
||||
"media.exr.dataWindow": "0,0,1919,1079",
|
||||
"media.exr.displayWindow": "0,0,1919,1079",
|
||||
"media.exr.dwaCompressionLevel": "90",
|
||||
"media.exr.lineOrder": "0",
|
||||
"media.exr.pixelAspectRatio": "1",
|
||||
"media.exr.screenWindowCenter": "0,0",
|
||||
"media.exr.screenWindowWidth": "1",
|
||||
"media.exr.type": "scanlineimage",
|
||||
"media.exr.version": "1",
|
||||
"media.input.bitsperchannel": "16-bit half float",
|
||||
"media.input.ctime": "2022-04-21 11:56:03",
|
||||
"media.input.filename": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P01/MER_sq001_sh020_P01.0997.exr",
|
||||
"media.input.filereader": "exr",
|
||||
"media.input.filesize": "1235182",
|
||||
"media.input.frame": "1",
|
||||
"media.input.frame_rate": "23.976",
|
||||
"media.input.height": "1080",
|
||||
"media.input.mtime": "2022-03-06 10:14:41",
|
||||
"media.input.timecode": "02:00:00:00",
|
||||
"media.input.width": "1920",
|
||||
"media.nuke.full_layer_names": "0",
|
||||
"media.nuke.node_hash": "ffffffffffffffff",
|
||||
"media.nuke.version": "12.2v3",
|
||||
"openpype.source.colourtransform": "ACES - ACES2065-1",
|
||||
"openpype.source.height": 1080,
|
||||
"openpype.source.pixelAspect": 1.0,
|
||||
"openpype.source.width": 1920,
|
||||
"padding": 4
|
||||
},
|
||||
"name": "",
|
||||
"available_range": {
|
||||
"OTIO_SCHEMA": "TimeRange.1",
|
||||
"duration": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976,
|
||||
"value": 59.0
|
||||
},
|
||||
"start_time": {
|
||||
"OTIO_SCHEMA": "RationalTime.1",
|
||||
"rate": 23.976,
|
||||
"value": 997.0
|
||||
}
|
||||
},
|
||||
"available_image_bounds": null,
|
||||
"target_url_base": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P01\\",
|
||||
"name_prefix": "MER_sq001_sh020_P01.",
|
||||
"name_suffix": ".exr",
|
||||
"start_frame": 997,
|
||||
"frame_step": 1,
|
||||
"rate": 23.976,
|
||||
"frame_zero_padding": 4,
|
||||
"missing_frame_policy": "error"
|
||||
}
|
||||
},
|
||||
"active_media_reference_key": "DEFAULT_MEDIA"
|
||||
}
|
||||
|
|
@ -166,3 +166,24 @@ def test_img_sequence_relative_source_range():
|
|||
"legacy_img_sequence.json",
|
||||
expected_data
|
||||
)
|
||||
|
||||
def test_img_sequence_conform_to_23_976fps():
|
||||
"""
|
||||
Img sequence clip
|
||||
available files = 997-1047 23.976fps
|
||||
source_range = 997-1055 23.976024627685547fps
|
||||
"""
|
||||
expected_data = {
|
||||
'mediaIn': 997,
|
||||
'mediaOut': 1047,
|
||||
'handleStart': 0,
|
||||
'handleEnd': 8,
|
||||
'speed': 1.0
|
||||
}
|
||||
|
||||
_check_expected_retimed_values(
|
||||
"img_seq_23.976_metadata.json",
|
||||
expected_data,
|
||||
handle_start=0,
|
||||
handle_end=8,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue