mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
🎨 add helpers for getting files
This commit is contained in:
parent
f4169769ac
commit
595a3546f3
3 changed files with 98 additions and 3 deletions
|
|
@ -2,10 +2,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import re
|
||||
|
||||
# TC003 is there because Path in TYPECHECKING will fail in tests
|
||||
from pathlib import Path # noqa: TC003
|
||||
from typing import ClassVar, Optional
|
||||
from typing import ClassVar, Generator, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
|
|
@ -109,6 +110,51 @@ class FileLocations(TraitBase):
|
|||
id: ClassVar[str] = "ayon.content.FileLocations.v1"
|
||||
file_paths: list[FileLocation] = Field(..., title="File Path")
|
||||
|
||||
def get_files(self) -> Generator[Path, None, None]:
|
||||
"""Get all file paths from the trait.
|
||||
|
||||
This method will return all file paths from the trait.
|
||||
|
||||
Yeilds:
|
||||
Path: List of file paths.
|
||||
|
||||
"""
|
||||
for file_location in self.file_paths:
|
||||
yield file_location.file_path
|
||||
|
||||
def get_file_for_frame(
|
||||
self,
|
||||
frame: int,
|
||||
sequence_trait: Optional[Sequence] = None,
|
||||
) -> Optional[FileLocation]:
|
||||
"""Get file location for a frame.
|
||||
|
||||
This method will return the file location for a given frame. If the
|
||||
frame is not found in the file paths, it will return None.
|
||||
|
||||
Args:
|
||||
frame (int): Frame to get the file location for.
|
||||
sequence_trait (Sequence): Sequence trait to get the
|
||||
frame range specs from.
|
||||
|
||||
Returns:
|
||||
Optional[FileLocation]: File location for the frame.
|
||||
|
||||
"""
|
||||
frame_regex = r"\.(?P<frame>(?P<padding>0*)\d+)\.\D+\d?$"
|
||||
if sequence_trait and sequence_trait.frame_regex:
|
||||
frame_regex = sequence_trait.frame_regex
|
||||
|
||||
re.compile(frame_regex)
|
||||
|
||||
for file_path in self.get_files():
|
||||
result = re.search(frame_regex, file_path.name)
|
||||
if result:
|
||||
frame_index = int(result.group("frame"))
|
||||
if frame_index == frame:
|
||||
return file_path
|
||||
return None
|
||||
|
||||
def validate(self, representation: Representation) -> None:
|
||||
"""Validate the trait.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from enum import Enum, auto
|
|||
from typing import TYPE_CHECKING, ClassVar, Optional
|
||||
|
||||
import clique
|
||||
from pydantic import Field
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
from .trait import MissingTraitError, TraitBase, TraitValidationError
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ class Sequence(TraitBase):
|
|||
sequence.
|
||||
frame_padding (int): Frame padding.
|
||||
frame_regex (str): Frame regex - regular expression to match
|
||||
frame numbers.
|
||||
frame numbers. Must include 'frame' named group.
|
||||
frame_spec (str): Frame list specification of frames. This takes
|
||||
string like "1-10,20-30,40-50" etc.
|
||||
|
||||
|
|
@ -132,6 +132,15 @@ class Sequence(TraitBase):
|
|||
frame_regex: Optional[str] = Field(None, title="Frame Regex")
|
||||
frame_spec: Optional[str] = Field(None, title="Frame Specification")
|
||||
|
||||
@field_validator("frame_regex")
|
||||
@classmethod
|
||||
def validate_frame_regex(cls, v: Optional[str]) -> str:
|
||||
"""Validate frame regex."""
|
||||
if v is not None and "?P<frame>" not in v:
|
||||
msg = "Frame regex must include 'frame' named group"
|
||||
raise ValueError(msg)
|
||||
return v
|
||||
|
||||
def validate(self, representation: Representation) -> None:
|
||||
"""Validate the trait."""
|
||||
super().validate(representation)
|
||||
|
|
|
|||
|
|
@ -139,3 +139,43 @@ def test_file_locations_validation() -> None:
|
|||
|
||||
with pytest.raises(TraitValidationError):
|
||||
representation.validate()
|
||||
|
||||
def test_get_file_from_frame() -> None:
|
||||
"""Test get_file_from_frame method."""
|
||||
file_locations_list = [
|
||||
FileLocation(
|
||||
file_path=Path(f"/path/to/file.{frame}.exr"),
|
||||
file_size=1024,
|
||||
file_hash=None,
|
||||
)
|
||||
for frame in range(1001, 1051)
|
||||
]
|
||||
|
||||
file_locations_trait: FileLocations = FileLocations(
|
||||
file_paths=file_locations_list)
|
||||
|
||||
assert file_locations_trait.get_file_for_frame(frame=1001) == \
|
||||
file_locations_list[0].file_path
|
||||
assert file_locations_trait.get_file_for_frame(frame=1050) == \
|
||||
file_locations_list[-1].file_path
|
||||
assert file_locations_trait.get_file_for_frame(frame=1100) is None
|
||||
|
||||
# test with custom regex
|
||||
sequence = Sequence(
|
||||
frame_padding=4,
|
||||
frame_regex=r"boo_(?P<frame>\d+)\.exr")
|
||||
file_locations_list = [
|
||||
FileLocation(
|
||||
file_path=Path(f"/path/to/boo_{frame}.exr"),
|
||||
file_size=1024,
|
||||
file_hash=None,
|
||||
)
|
||||
for frame in range(1001, 1051)
|
||||
]
|
||||
|
||||
file_locations_trait: FileLocations = FileLocations(
|
||||
file_paths=file_locations_list)
|
||||
|
||||
assert file_locations_trait.get_file_for_frame(
|
||||
frame=1001, sequence_trait=sequence) == \
|
||||
file_locations_list[0].file_path
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue