mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Refactor Resolve media loading logic for single files and sequences.
- Refactored file path handling for importing media. - Updated method to determine if the file is a sequence. - Improved handling of frame numbers in file paths.
This commit is contained in:
parent
21c86742d4
commit
7b1c4a8a08
1 changed files with 75 additions and 28 deletions
|
|
@ -1,17 +1,19 @@
|
|||
import json
|
||||
import copy
|
||||
import contextlib
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
from typing import Union, List, Optional, TypedDict
|
||||
from typing import Union, List, Optional, TypedDict, Tuple
|
||||
|
||||
from ayon_api import version_is_latest
|
||||
from ayon_core.lib import StringTemplate
|
||||
from ayon_core.pipeline.colorspace import get_remapped_colorspace_to_native
|
||||
from ayon_core.pipeline import (
|
||||
Anatomy,
|
||||
LoaderPlugin,
|
||||
get_representation_path,
|
||||
registered_host
|
||||
)
|
||||
from ayon_core.pipeline.load import get_representation_path_with_anatomy
|
||||
from ayon_core.lib.transcoding import (
|
||||
VIDEO_EXTENSIONS,
|
||||
IMAGE_EXTENSIONS
|
||||
|
|
@ -21,6 +23,9 @@ from ayon_resolve.api import lib
|
|||
from ayon_resolve.api.pipeline import AVALON_CONTAINER_ID
|
||||
|
||||
|
||||
FRAME_SPLITTER = "__frame_splitter__"
|
||||
|
||||
|
||||
class MetadataEntry(TypedDict):
|
||||
"""Metadata entry is dict with {"name": "key", "value: "value"}"""
|
||||
name: str
|
||||
|
|
@ -188,6 +193,7 @@ class LoadMedia(LoaderPlugin):
|
|||
self.timeline = lib.get_current_timeline()
|
||||
|
||||
representation = context["representation"]
|
||||
self._project_name = context["project"]["name"]
|
||||
|
||||
project = lib.get_current_project()
|
||||
media_pool = project.GetMediaPool()
|
||||
|
|
@ -221,10 +227,17 @@ class LoadMedia(LoaderPlugin):
|
|||
media_pool.SetCurrentFolder(folder)
|
||||
|
||||
# Import media
|
||||
path = self._get_filepath(context)
|
||||
items = media_pool.ImportMedia([path])
|
||||
# Resolve API: ImportMedia function requires a list of dictionaries
|
||||
# with keys "FilePath", "StartIndex" and "EndIndex" for sequences
|
||||
# but only string with absolute path for single files.
|
||||
is_sequence, file_info = self._get_file_info(context)
|
||||
if is_sequence:
|
||||
items = media_pool.ImportMedia([file_info])
|
||||
else:
|
||||
items = media_pool.ImportMedia([file_info["FilePath"]])
|
||||
|
||||
assert len(items) == 1, "Must import only one media item"
|
||||
|
||||
item = items[0]
|
||||
|
||||
self._set_metadata(item, context)
|
||||
|
|
@ -384,40 +397,74 @@ class LoadMedia(LoaderPlugin):
|
|||
value_formatted = StringTemplate(value).format_strict(context)
|
||||
media_pool_item.SetClipProperty(clip_property, value_formatted)
|
||||
|
||||
def _get_filepath(self, context: dict) -> Union[str, dict]:
|
||||
def _get_file_info(self, context: dict) -> Tuple[bool, Union[str, dict]]:
|
||||
"""Return file info for Resolve ImportMedia.
|
||||
|
||||
Args:
|
||||
context (dict): The context dictionary.
|
||||
|
||||
Returns:
|
||||
Tuple[bool, Union[str, dict]]: A tuple of whether the file is a
|
||||
sequence and the file info dictionary.
|
||||
"""
|
||||
|
||||
representation = context["representation"]
|
||||
is_sequence = bool(representation["context"].get("frame"))
|
||||
if not is_sequence:
|
||||
return get_representation_path(representation)
|
||||
anatomy = Anatomy(self._project_name)
|
||||
|
||||
version = context["version"]
|
||||
# Get path to representation with correct frame number
|
||||
repre_path = get_representation_path_with_anatomy(
|
||||
representation, anatomy)
|
||||
|
||||
# Get the start and end frame of the image sequence, incl. handles
|
||||
frame_start = version["data"].get("frameStart", 0)
|
||||
frame_end = version["data"].get("frameEnd", 0)
|
||||
handle_start = version["data"].get("handleStart", 0)
|
||||
handle_end = version["data"].get("handleEnd", 0)
|
||||
frame_start_handle = frame_start - handle_start
|
||||
frame_end_handle = frame_end + handle_end
|
||||
padding = len(representation["context"].get("frame"))
|
||||
first_frame = representation["context"].get("frame")
|
||||
|
||||
# We format the frame number to the required token. To do so
|
||||
# we in-place change the representation context data to format the path
|
||||
# with that replaced data
|
||||
representation = copy.deepcopy(representation)
|
||||
representation["context"]["frame"] = f"%0{padding}d"
|
||||
path = get_representation_path(representation)
|
||||
is_sequence = False
|
||||
# is not sequence
|
||||
if first_frame is None:
|
||||
return (
|
||||
is_sequence, {"FilePath": repre_path}
|
||||
)
|
||||
|
||||
# This is sequence
|
||||
is_sequence = True
|
||||
repre_files = [
|
||||
file["path"].format(root=anatomy.roots)
|
||||
for file in representation["files"]
|
||||
]
|
||||
|
||||
# Change frame in representation context to get path with frame
|
||||
# splitter.
|
||||
representation["context"]["frame"] = FRAME_SPLITTER
|
||||
frame_repre_path = get_representation_path_with_anatomy(
|
||||
representation, anatomy
|
||||
)
|
||||
frame_repre_path = Path(frame_repre_path)
|
||||
repre_dir, repre_filename = (
|
||||
frame_repre_path.parent, frame_repre_path.name)
|
||||
# Get sequence prefix and suffix
|
||||
file_prefix, file_suffix = repre_filename.split(FRAME_SPLITTER)
|
||||
# Get frame number from path as string to get frame padding
|
||||
frame_str = str(repre_path)[len(file_prefix):][:len(file_suffix)]
|
||||
frame_padding = len(frame_str)
|
||||
|
||||
file_name = f"{file_prefix}%0{frame_padding}d{file_suffix}"
|
||||
|
||||
abs_filepath = Path(repre_dir, file_name)
|
||||
|
||||
start_index = int(first_frame)
|
||||
end_index = int(int(first_frame) + len(repre_files) - 1)
|
||||
|
||||
# See Resolve API, to import for example clip "file_[001-100].dpx":
|
||||
# ImportMedia([{"FilePath":"file_%03d.dpx",
|
||||
# "StartIndex":1,
|
||||
# "EndIndex":100}])
|
||||
return {
|
||||
"FilePath": path,
|
||||
"StartIndex": frame_start_handle,
|
||||
"EndIndex": frame_end_handle
|
||||
}
|
||||
return (
|
||||
is_sequence,
|
||||
{
|
||||
"FilePath": abs_filepath.as_posix(),
|
||||
"StartIndex": start_index,
|
||||
"EndIndex": end_index,
|
||||
}
|
||||
)
|
||||
|
||||
def _get_colorspace(self, representation: dict) -> Optional[str]:
|
||||
"""Return Resolve native colorspace from OCIO colorspace data.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue