mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
🐛 fix template data
This commit is contained in:
parent
365575604c
commit
1dd3c00eb5
2 changed files with 219 additions and 41 deletions
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
|
from copy import deepcopy
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Any, List
|
from typing import TYPE_CHECKING, Any, List
|
||||||
|
|
@ -47,8 +48,8 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
from ayon_core.pipeline import Anatomy
|
from ayon_core.pipeline import Anatomy
|
||||||
from ayon_core.pipeline.anatomy.templates import (
|
from ayon_core.pipeline.anatomy.templates import (
|
||||||
TemplateItem as AnatomyTemplateItem,
|
TemplateItem as AnatomyTemplateItem, AnatomyStringTemplate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
@ -203,23 +204,43 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
"Instance has no persistent representations. Skipping")
|
"Instance has no persistent representations. Skipping")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 3) get template and template data
|
|
||||||
template: str = self.get_publish_template(instance)
|
|
||||||
|
|
||||||
# 4) initialize OperationsSession()
|
|
||||||
op_session = OperationsSession()
|
op_session = OperationsSession()
|
||||||
|
|
||||||
# 5) Prepare product
|
|
||||||
product_entity = self.prepare_product(instance, op_session)
|
product_entity = self.prepare_product(instance, op_session)
|
||||||
|
|
||||||
# 6) Prepare version
|
|
||||||
version_entity = self.prepare_version(
|
version_entity = self.prepare_version(
|
||||||
instance, op_session, product_entity
|
instance, op_session, product_entity
|
||||||
)
|
)
|
||||||
instance.data["versionEntity"] = version_entity
|
instance.data["versionEntity"] = version_entity
|
||||||
|
|
||||||
instance_template_data: dict[str, str] = {}
|
transfers = self.get_transfers_from_representations(
|
||||||
|
instance, representations)
|
||||||
|
|
||||||
|
def get_transfers_from_representations(
|
||||||
|
self,
|
||||||
|
instance: pyblish.api.Instance,
|
||||||
|
representations: list[Representation]) -> list[TransferItem]:
|
||||||
|
"""Get transfers from representations.
|
||||||
|
|
||||||
|
This method will go through all representations and prepare transfers
|
||||||
|
based on the traits they contain. First it will validate the
|
||||||
|
representation, and then it will prepare template data for the
|
||||||
|
representation. It specifically handles FileLocations, FileLocation,
|
||||||
|
Bundle, Sequence and UDIM traits.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
instance (pyblish.api.Instance): Instance to process.
|
||||||
|
representations (list[Representation]): List of representations.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[TransferItem]: List of transfers.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
PublishError: If representation is invalid.
|
||||||
|
|
||||||
|
"""
|
||||||
|
template: str = self.get_publish_template(instance)
|
||||||
|
instance_template_data: dict[str, str] = {}
|
||||||
transfers: list[TransferItem] = []
|
transfers: list[TransferItem] = []
|
||||||
# prepare template and data to format it
|
# prepare template and data to format it
|
||||||
for representation in representations:
|
for representation in representations:
|
||||||
|
|
@ -235,6 +256,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
template_data = self.get_template_data_from_representation(
|
template_data = self.get_template_data_from_representation(
|
||||||
representation, instance)
|
representation, instance)
|
||||||
# add instance based template data
|
# add instance based template data
|
||||||
|
|
||||||
template_data.update(instance_template_data)
|
template_data.update(instance_template_data)
|
||||||
|
|
||||||
# treat Variant as `output` in template data
|
# treat Variant as `output` in template data
|
||||||
|
|
@ -246,7 +268,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
template_item = TemplateItem(
|
template_item = TemplateItem(
|
||||||
anatomy=instance.context.data["anatomy"],
|
anatomy=instance.context.data["anatomy"],
|
||||||
template=template,
|
template=template,
|
||||||
template_data=template_data,
|
template_data=copy.deepcopy(template_data),
|
||||||
template_object=self.get_publish_template_object(instance),
|
template_object=self.get_publish_template_object(instance),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -273,7 +295,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
self.get_transfers_from_bundle(
|
self.get_transfers_from_bundle(
|
||||||
representation, template_item, transfers
|
representation, template_item, transfers
|
||||||
)
|
)
|
||||||
|
return transfers
|
||||||
|
|
||||||
def _get_relative_to_root_original_dirname(
|
def _get_relative_to_root_original_dirname(
|
||||||
self, instance: pyblish.api.Instance) -> str:
|
self, instance: pyblish.api.Instance) -> str:
|
||||||
|
|
@ -668,6 +690,8 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
"""
|
"""
|
||||||
template_data = copy.deepcopy(instance.data["anatomyData"])
|
template_data = copy.deepcopy(instance.data["anatomyData"])
|
||||||
template_data["representation"] = representation.name
|
template_data["representation"] = representation.name
|
||||||
|
template_data["version"] = instance.data["version"]
|
||||||
|
template_data["hierarchy"] = instance.data["hierarchy"]
|
||||||
|
|
||||||
# add colorspace data to template data
|
# add colorspace data to template data
|
||||||
if representation.contains_trait(ColorManaged):
|
if representation.contains_trait(ColorManaged):
|
||||||
|
|
@ -770,23 +794,26 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
if template_padding > dst_padding:
|
if template_padding > dst_padding:
|
||||||
dst_padding = template_padding
|
dst_padding = template_padding
|
||||||
|
|
||||||
# add template path and the data to resolve it
|
|
||||||
representation.add_trait(TemplatePath(
|
|
||||||
template=template_item.template,
|
|
||||||
data=template_item.template_data
|
|
||||||
))
|
|
||||||
|
|
||||||
# go through all frames in the sequence
|
# go through all frames in the sequence
|
||||||
# find their corresponding file locations
|
# find their corresponding file locations
|
||||||
# format their template and add them to transfers
|
# format their template and add them to transfers
|
||||||
for frame in frames:
|
for frame in frames:
|
||||||
template_item.template_data["frame"] = frame
|
|
||||||
template_filled = path_template_object.format_strict(
|
|
||||||
template_item.template_data
|
|
||||||
)
|
|
||||||
file_loc: FileLocation = representation.get_trait(
|
file_loc: FileLocation = representation.get_trait(
|
||||||
FileLocations).get_file_location_for_frame(
|
FileLocations).get_file_location_for_frame(
|
||||||
frame, sequence)
|
frame, sequence)
|
||||||
|
|
||||||
|
template_item.template_data["frame"] = frame
|
||||||
|
template_item.template_data["ext"] = (
|
||||||
|
file_loc.file_path.suffix
|
||||||
|
)
|
||||||
|
template_filled = path_template_object.format_strict(
|
||||||
|
template_item.template_data
|
||||||
|
)
|
||||||
|
|
||||||
|
# add used values to the template data
|
||||||
|
used_values: dict = template_filled.used_values
|
||||||
|
template_item.template_data.update(used_values)
|
||||||
|
|
||||||
transfers.append(
|
transfers.append(
|
||||||
TransferItem(
|
TransferItem(
|
||||||
source=file_loc.file_path,
|
source=file_loc.file_path,
|
||||||
|
|
@ -799,6 +826,12 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# add template path and the data to resolve it
|
||||||
|
representation.add_trait(TemplatePath(
|
||||||
|
template=template_item.template,
|
||||||
|
data=template_item.template_data
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_transfers_from_udim(
|
def get_transfers_from_udim(
|
||||||
|
|
@ -819,13 +852,23 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
udim: UDIM = representation.get_trait(UDIM)
|
udim: UDIM = representation.get_trait(UDIM)
|
||||||
|
path_template_object: AnatomyStringTemplate = (
|
||||||
|
template_item.template_object["path"]
|
||||||
|
)
|
||||||
for file_loc in representation.get_trait(
|
for file_loc in representation.get_trait(
|
||||||
FileLocations).file_paths:
|
FileLocations).file_paths:
|
||||||
template_item.template_data["udim"] = (
|
template_item.template_data["udim"] = (
|
||||||
udim.get_udim_from_file_location(file_loc)
|
udim.get_udim_from_file_location(file_loc)
|
||||||
)
|
)
|
||||||
template_filled = template_item.template.format(
|
|
||||||
**template_item.template_data)
|
template_filled = path_template_object.format_strict(
|
||||||
|
template_item.template_data
|
||||||
|
)
|
||||||
|
|
||||||
|
# add used values to the template data
|
||||||
|
used_values: dict = template_filled.used_values
|
||||||
|
template_item.template_data.update(used_values)
|
||||||
|
|
||||||
transfers.append(
|
transfers.append(
|
||||||
TransferItem(
|
TransferItem(
|
||||||
source=file_loc.file_path,
|
source=file_loc.file_path,
|
||||||
|
|
@ -861,7 +904,12 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
template_item (TemplateItem): Template item.
|
template_item (TemplateItem): Template item.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
path_template_object = template_item.template_object["path"]
|
path_template_object: AnatomyStringTemplate = (
|
||||||
|
template_item.template_object["path"]
|
||||||
|
)
|
||||||
|
template_item.template_data["ext"] = (
|
||||||
|
representation.get_trait(FileLocation).file_path.suffix
|
||||||
|
)
|
||||||
template_item.template_data.pop("frame", None)
|
template_item.template_data.pop("frame", None)
|
||||||
with contextlib.suppress(MissingTraitError):
|
with contextlib.suppress(MissingTraitError):
|
||||||
udim = representation.get_trait(UDIM)
|
udim = representation.get_trait(UDIM)
|
||||||
|
|
@ -870,6 +918,11 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
template_filled = path_template_object.format_strict(
|
template_filled = path_template_object.format_strict(
|
||||||
template_item.template_data
|
template_item.template_data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# add used values to the template data
|
||||||
|
used_values: dict = template_filled.used_values
|
||||||
|
template_item.template_data.update(used_values)
|
||||||
|
|
||||||
file_loc: FileLocation = representation.get_trait(FileLocation)
|
file_loc: FileLocation = representation.get_trait(FileLocation)
|
||||||
transfers.append(
|
transfers.append(
|
||||||
TransferItem(
|
TransferItem(
|
||||||
|
|
@ -878,7 +931,7 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
size=file_loc.file_size,
|
size=file_loc.file_size,
|
||||||
checksum=file_loc.file_hash,
|
checksum=file_loc.file_hash,
|
||||||
template=template_item.template,
|
template=template_item.template,
|
||||||
template_data=template_item.template_data.copy(),
|
template_data=template_item.template_data,
|
||||||
representation=representation,
|
representation=representation,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -928,3 +981,4 @@ class IntegrateTraits(pyblish.api.InstancePlugin):
|
||||||
IntegrateTraits.get_transfers_from_bundle(
|
IntegrateTraits.get_transfers_from_bundle(
|
||||||
sub_representation, template_item, transfers
|
sub_representation, template_item, transfers
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,16 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
import pytest
|
import pytest
|
||||||
|
import pytest_ayon
|
||||||
|
from ayon_api.operations import (
|
||||||
|
OperationsSession,
|
||||||
|
)
|
||||||
|
from ayon_core.pipeline.anatomy import Anatomy
|
||||||
from ayon_core.pipeline.traits import (
|
from ayon_core.pipeline.traits import (
|
||||||
FileLocation,
|
FileLocation,
|
||||||
FileLocations,
|
FileLocations,
|
||||||
|
|
@ -18,6 +24,7 @@ from ayon_core.pipeline.traits import (
|
||||||
Sequence,
|
Sequence,
|
||||||
Transient,
|
Transient,
|
||||||
)
|
)
|
||||||
|
from ayon_core.pipeline.version_start import get_versioning_start
|
||||||
|
|
||||||
# Tagged,
|
# Tagged,
|
||||||
# TemplatePath,
|
# TemplatePath,
|
||||||
|
|
@ -26,6 +33,8 @@ from ayon_core.settings import get_project_settings
|
||||||
|
|
||||||
PNG_FILE_B64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVR4AWNgAAAAAgABc3UBGAAAAABJRU5ErkJggg==" # noqa: E501
|
PNG_FILE_B64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVR4AWNgAAAAAgABc3UBGAAAAABJRU5ErkJggg==" # noqa: E501
|
||||||
SEQUENCE_LENGTH = 10
|
SEQUENCE_LENGTH = 10
|
||||||
|
CURRENT_TIME = time.time()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def single_file(tmp_path_factory: pytest.TempPathFactory) -> Path:
|
def single_file(tmp_path_factory: pytest.TempPathFactory) -> Path:
|
||||||
|
|
@ -48,7 +57,7 @@ def sequence_files(tmp_path_factory: pytest.TempPathFactory) -> list[Path]:
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def mock_context(
|
def mock_context(
|
||||||
project: object,
|
project: pytest_ayon.ProjectInfo,
|
||||||
single_file: Path,
|
single_file: Path,
|
||||||
sequence_files: list[Path]) -> pyblish.api.Context:
|
sequence_files: list[Path]) -> pyblish.api.Context:
|
||||||
"""Return a mock instance.
|
"""Return a mock instance.
|
||||||
|
|
@ -56,18 +65,6 @@ def mock_context(
|
||||||
This is mocking pyblish context for testing. It is using real AYON project
|
This is mocking pyblish context for testing. It is using real AYON project
|
||||||
thanks to the ``project`` fixture.
|
thanks to the ``project`` fixture.
|
||||||
|
|
||||||
This returns following data::
|
|
||||||
|
|
||||||
project_name: str
|
|
||||||
project_code: str
|
|
||||||
project_root_folders: dict[str, str]
|
|
||||||
folder: IdNamePair
|
|
||||||
task: IdNamePair
|
|
||||||
product: IdNamePair
|
|
||||||
version: IdNamePair
|
|
||||||
representations: List[IdNamePair]
|
|
||||||
links: List[str]
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
project (object): The project info. It is `ProjectInfo` object
|
project (object): The project info. It is `ProjectInfo` object
|
||||||
returned by pytest fixture.
|
returned by pytest fixture.
|
||||||
|
|
@ -75,25 +72,68 @@ def mock_context(
|
||||||
sequence_files (list[Path]): The paths to a sequence of image files.
|
sequence_files (list[Path]): The paths to a sequence of image files.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
anatomy = Anatomy(project.project_name)
|
||||||
context = pyblish.api.Context()
|
context = pyblish.api.Context()
|
||||||
context.data["projectName"] = project.project_name
|
context.data["projectName"] = project.project_name
|
||||||
context.data["hostName"] = "test_host"
|
context.data["hostName"] = "test_host"
|
||||||
context.data["project_settings"] = get_project_settings(
|
context.data["project_settings"] = get_project_settings(
|
||||||
project.project_name)
|
project.project_name)
|
||||||
|
context.data["anatomy"] = anatomy
|
||||||
|
context.data["time"] = CURRENT_TIME
|
||||||
|
context.data["user"] = "test_user"
|
||||||
|
context.data["machine"] = "test_machine"
|
||||||
|
context.data["fps"] = 25
|
||||||
|
|
||||||
instance = context.create_instance("mock_instance")
|
instance = context.create_instance("mock_instance")
|
||||||
|
instance.data["source"] = "test_source"
|
||||||
|
instance.data["families"] = ["render"]
|
||||||
instance.data["anatomyData"] = {
|
instance.data["anatomyData"] = {
|
||||||
"project": project.project_name,
|
"project": {
|
||||||
|
"name": project.project_name,
|
||||||
|
"code": project.project_code
|
||||||
|
},
|
||||||
"task": {
|
"task": {
|
||||||
"name": project.task.name,
|
"name": project.task.name,
|
||||||
"type": "test" # pytest-ayon doesn't return the task type yet
|
"type": "test" # pytest-ayon doesn't return the task type yet
|
||||||
}
|
},
|
||||||
|
"folder": {
|
||||||
|
"name": project.folder.name,
|
||||||
|
"type": "test" # pytest-ayon doesn't return the folder type yet
|
||||||
|
},
|
||||||
|
"product": {
|
||||||
|
"name": project.product.name,
|
||||||
|
"type": "test" # pytest-ayon doesn't return the product type yet
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
instance.data["folderEntity"] = project.folder_entity
|
||||||
instance.data["productType"] = "test_product"
|
instance.data["productType"] = "test_product"
|
||||||
|
instance.data["productName"] = project.product.name
|
||||||
|
instance.data["anatomy"] = anatomy
|
||||||
|
instance.data["comment"] = "test_comment"
|
||||||
|
|
||||||
instance.data["integrate"] = True
|
instance.data["integrate"] = True
|
||||||
instance.data["farm"] = False
|
instance.data["farm"] = False
|
||||||
|
|
||||||
|
parents = project.folder_entity["path"].lstrip("/").split("/")
|
||||||
|
|
||||||
|
hierarchy = ""
|
||||||
|
if parents:
|
||||||
|
hierarchy = "/".join(parents)
|
||||||
|
|
||||||
|
instance.data["hierarchy"] = hierarchy
|
||||||
|
|
||||||
|
version_number = get_versioning_start(
|
||||||
|
context.data["projectName"],
|
||||||
|
instance.context.data["hostName"],
|
||||||
|
task_name=project.task.name,
|
||||||
|
task_type="test",
|
||||||
|
product_type=instance.data["productType"],
|
||||||
|
product_name=instance.data["productName"]
|
||||||
|
)
|
||||||
|
|
||||||
|
instance.data["version"] = version_number
|
||||||
|
|
||||||
_file_size = len(base64.b64decode(PNG_FILE_B64))
|
_file_size = len(base64.b64decode(PNG_FILE_B64))
|
||||||
file_locations = [
|
file_locations = [
|
||||||
FileLocation(
|
FileLocation(
|
||||||
|
|
@ -121,7 +161,7 @@ def mock_context(
|
||||||
),
|
),
|
||||||
Sequence(
|
Sequence(
|
||||||
frame_padding=4,
|
frame_padding=4,
|
||||||
frame_regex=r"^img\.(\d{4})\.png$",
|
frame_regex=r"^img\.(?P<frame>\d{4})\.png$",
|
||||||
),
|
),
|
||||||
FileLocations(
|
FileLocations(
|
||||||
file_paths=file_locations,
|
file_paths=file_locations,
|
||||||
|
|
@ -176,3 +216,87 @@ def test_filter_lifecycle() -> None:
|
||||||
|
|
||||||
assert len(filtered) == 1
|
assert len(filtered) == 1
|
||||||
assert filtered[0] == persistent_representation
|
assert filtered[0] == persistent_representation
|
||||||
|
|
||||||
|
|
||||||
|
def test_prepare_product(
|
||||||
|
project: pytest_ayon.ProjectInfo,
|
||||||
|
mock_context: pyblish.api.Context) -> None:
|
||||||
|
"""Test prepare_product."""
|
||||||
|
integrator = IntegrateTraits()
|
||||||
|
op_session = OperationsSession()
|
||||||
|
product = integrator.prepare_product(mock_context[0], op_session)
|
||||||
|
|
||||||
|
assert product == {
|
||||||
|
"attrib": {},
|
||||||
|
"data": {
|
||||||
|
"families": ["default", "render"],
|
||||||
|
},
|
||||||
|
"folderId": project.folder_entity["id"],
|
||||||
|
"name": "renderMain",
|
||||||
|
"productType": "test_product",
|
||||||
|
"id": project.product_entity["id"],
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_prepare_version(
|
||||||
|
project: pytest_ayon.ProjectInfo,
|
||||||
|
mock_context: pyblish.api.Context) -> None:
|
||||||
|
"""Test prepare_version."""
|
||||||
|
integrator = IntegrateTraits()
|
||||||
|
op_session = OperationsSession()
|
||||||
|
product = integrator.prepare_product(mock_context[0], op_session)
|
||||||
|
version = integrator.prepare_version(
|
||||||
|
mock_context[0], op_session , product)
|
||||||
|
|
||||||
|
assert version == {
|
||||||
|
"attrib": {
|
||||||
|
"comment": "test_comment",
|
||||||
|
"families": ["default", "render"],
|
||||||
|
"fps": 25,
|
||||||
|
"machine": "test_machine",
|
||||||
|
"source": "test_source",
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"author": "test_user",
|
||||||
|
"time": CURRENT_TIME,
|
||||||
|
},
|
||||||
|
"id": project.version_entity["id"],
|
||||||
|
"productId": project.product_entity["id"],
|
||||||
|
"version": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_transfers_from_representation(
|
||||||
|
mock_context: pyblish.api.Context) -> None:
|
||||||
|
"""Test get_transfers_from_representation."""
|
||||||
|
integrator = IntegrateTraits()
|
||||||
|
|
||||||
|
instance = mock_context[0]
|
||||||
|
representations: list[Representation] = instance.data[
|
||||||
|
"representations_with_traits"]
|
||||||
|
transfers = integrator.get_transfers_from_representations(
|
||||||
|
instance, representations)
|
||||||
|
|
||||||
|
assert transfers == [
|
||||||
|
{
|
||||||
|
"file_path": Path("test"),
|
||||||
|
"file_size": 1234,
|
||||||
|
"traits": [
|
||||||
|
"Persistent",
|
||||||
|
"Image",
|
||||||
|
"MimeType"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file_path": Path("test"),
|
||||||
|
"file_size": 1234,
|
||||||
|
"traits": [
|
||||||
|
"Persistent",
|
||||||
|
"FrameRanged",
|
||||||
|
"Sequence",
|
||||||
|
"FileLocations",
|
||||||
|
"Image",
|
||||||
|
"PixelBased",
|
||||||
|
"MimeType"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue