ayon-core/openpype/lib/editorial.py

168 lines
4.4 KiB
Python

import os
import re
import clique
from .import_utils import discover_host_vendor_module
try:
from opentimelineio import opentime as _ot
except ImportError:
_ot = discover_host_vendor_module("opentimelineio.opentime")
def otio_range_to_frame_range(otio_range):
start = _ot.to_frames(
otio_range.start_time, otio_range.start_time.rate)
end = start + _ot.to_frames(
otio_range.duration, otio_range.duration.rate) - 1
return start, end
def otio_range_with_handles(otio_range, instance):
handle_start = instance.data["handleStart"]
handle_end = instance.data["handleEnd"]
handles_duration = handle_start + handle_end
fps = float(otio_range.start_time.rate)
start = _ot.to_frames(otio_range.start_time, fps)
duration = _ot.to_frames(otio_range.duration, fps)
return _ot.TimeRange(
start_time=_ot.RationalTime((start - handle_start), fps),
duration=_ot.RationalTime((duration + handles_duration), fps)
)
def is_overlapping_otio_ranges(test_otio_range, main_otio_range, strict=False):
test_start, test_end = otio_range_to_frame_range(test_otio_range)
main_start, main_end = otio_range_to_frame_range(main_otio_range)
covering_exp = bool(
(test_start <= main_start) and (test_end >= main_end)
)
inside_exp = bool(
(test_start >= main_start) and (test_end <= main_end)
)
overlaying_right_exp = bool(
(test_start <= main_end) and (test_end >= main_end)
)
overlaying_left_exp = bool(
(test_end >= main_start) and (test_start <= main_start)
)
if not strict:
return any((
covering_exp,
inside_exp,
overlaying_right_exp,
overlaying_left_exp
))
else:
return covering_exp
def convert_to_padded_path(path, padding):
"""
Return correct padding in sequence string
Args:
path (str): path url or simple file name
padding (int): number of padding
Returns:
type: string with reformated path
Example:
convert_to_padded_path("plate.%d.exr") > plate.%04d.exr
"""
if "%d" in path:
path = re.sub("%d", "%0{padding}d".format(padding=padding), path)
return path
def trim_media_range(media_range, source_range):
"""
Trim input media range with clip source range.
Args:
media_range (otio._ot._ot.TimeRange): available range of media
source_range (otio._ot._ot.TimeRange): clip required range
Returns:
otio._ot._ot.TimeRange: trimmed media range
"""
rw_media_start = _ot.RationalTime(
media_range.start_time.value + source_range.start_time.value,
media_range.start_time.rate
)
rw_media_duration = _ot.RationalTime(
source_range.duration.value,
media_range.duration.rate
)
return _ot.TimeRange(
rw_media_start, rw_media_duration)
def range_from_frames(start, duration, fps):
"""
Returns otio time range.
Args:
start (int): frame start
duration (int): frame duration
fps (float): frame range
Returns:
otio._ot._ot.TimeRange: crated range
"""
return _ot.TimeRange(
_ot.RationalTime(start, fps),
_ot.RationalTime(duration, fps)
)
def frames_to_secons(frames, framerate):
"""
Returning secons.
Args:
frames (int): frame
framerate (flaot): frame rate
Returns:
float: second value
"""
rt = _ot.from_frames(frames, framerate)
return _ot.to_seconds(rt)
def frames_to_timecode(frames, framerate):
rt = _ot.from_frames(frames, framerate)
return _ot.to_timecode(rt)
def make_sequence_collection(path, otio_range, metadata):
"""
Make collection from path otio range and otio metadata.
Args:
path (str): path to image sequence with `%d`
otio_range (otio._ot._ot.TimeRange): range to be used
metadata (dict): data where padding value can be found
Returns:
list: dir_path (str): path to sequence, collection object
"""
if "%" not in path:
return None
file_name = os.path.basename(path)
dir_path = os.path.dirname(path)
head = file_name.split("%")[0]
tail = os.path.splitext(file_name)[-1]
first, last = otio_range_to_frame_range(otio_range)
collection = clique.Collection(
head=head, tail=tail, padding=metadata["padding"])
collection.indexes.update([i for i in range(first, (last + 1))])
return dir_path, collection