mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #3419 from pypeclub/feature/OP-3419_Move-editorial-logic-to-pipeline
General: Move editorial lib to pipeline
This commit is contained in:
commit
c4bed47cd8
10 changed files with 391 additions and 304 deletions
|
|
@ -3,7 +3,10 @@ from types import NoneType
|
|||
import pyblish
|
||||
import openpype.hosts.flame.api as opfapi
|
||||
from openpype.hosts.flame.otio import flame_export
|
||||
import openpype.lib as oplib
|
||||
from openpype.pipeline.editorial import (
|
||||
is_overlapping_otio_ranges,
|
||||
get_media_range_with_retimes
|
||||
)
|
||||
|
||||
# # developer reload modules
|
||||
from pprint import pformat
|
||||
|
|
@ -279,7 +282,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
|
|||
|
||||
# HACK: it is here to serve for versions bellow 2021.1
|
||||
if not any([head, tail]):
|
||||
retimed_attributes = oplib.get_media_range_with_retimes(
|
||||
retimed_attributes = get_media_range_with_retimes(
|
||||
otio_clip, handle_start, handle_end)
|
||||
self.log.debug(
|
||||
">> retimed_attributes: {}".format(retimed_attributes))
|
||||
|
|
@ -378,7 +381,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
|
|||
continue
|
||||
if otio_clip.name not in segment.name.get_value():
|
||||
continue
|
||||
if oplib.is_overlapping_otio_ranges(
|
||||
if is_overlapping_otio_ranges(
|
||||
parent_range, timeline_range, strict=True):
|
||||
|
||||
# add pypedata marker to otio_clip metadata
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pyblish
|
||||
import openpype
|
||||
from openpype.pipeline.editorial import is_overlapping_otio_ranges
|
||||
from openpype.hosts.hiero import api as phiero
|
||||
from openpype.hosts.hiero.api.otio import hiero_export
|
||||
import hiero
|
||||
|
|
@ -275,7 +275,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
parent_range = otio_audio.range_in_parent()
|
||||
|
||||
# if any overaling clip found then return True
|
||||
if openpype.lib.is_overlapping_otio_ranges(
|
||||
if is_overlapping_otio_ranges(
|
||||
parent_range, timeline_range, strict=False):
|
||||
return True
|
||||
|
||||
|
|
@ -304,7 +304,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
continue
|
||||
self.log.debug("__ parent_range: {}".format(parent_range))
|
||||
self.log.debug("__ timeline_range: {}".format(timeline_range))
|
||||
if openpype.lib.is_overlapping_otio_ranges(
|
||||
if is_overlapping_otio_ranges(
|
||||
parent_range, timeline_range, strict=True):
|
||||
|
||||
# add pypedata marker to otio_clip metadata
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import re
|
|||
import os
|
||||
import contextlib
|
||||
from opentimelineio import opentime
|
||||
import openpype
|
||||
from openpype.pipeline.editorial import is_overlapping_otio_ranges
|
||||
|
||||
from ..otio import davinci_export as otio_export
|
||||
|
||||
|
|
@ -824,7 +824,7 @@ def get_otio_clip_instance_data(otio_timeline, timeline_item_data):
|
|||
continue
|
||||
if otio_clip.name not in timeline_item.GetName():
|
||||
continue
|
||||
if openpype.lib.is_overlapping_otio_ranges(
|
||||
if is_overlapping_otio_ranges(
|
||||
parent_range, timeline_range, strict=True):
|
||||
|
||||
# add pypedata marker to otio_clip metadata
|
||||
|
|
|
|||
|
|
@ -1,289 +1,102 @@
|
|||
import os
|
||||
import re
|
||||
import clique
|
||||
from .import_utils import discover_host_vendor_module
|
||||
"""Code related to editorial utility functions was moved
|
||||
to 'openpype.pipeline.editorial' please change your imports as soon as
|
||||
possible. File will be probably removed in OpenPype 3.14.*
|
||||
"""
|
||||
|
||||
try:
|
||||
import opentimelineio as otio
|
||||
from opentimelineio import opentime as _ot
|
||||
except ImportError:
|
||||
if not os.environ.get("AVALON_APP"):
|
||||
raise
|
||||
otio = discover_host_vendor_module("opentimelineio")
|
||||
_ot = discover_host_vendor_module("opentimelineio.opentime")
|
||||
import warnings
|
||||
import functools
|
||||
|
||||
|
||||
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)
|
||||
return start, end
|
||||
class EditorialDeprecatedWarning(DeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
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)
|
||||
def editorial_deprecated(func):
|
||||
"""Mark functions as deprecated.
|
||||
|
||||
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):
|
||||
It will result in a warning being emitted when the function is used.
|
||||
"""
|
||||
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
|
||||
@functools.wraps(func)
|
||||
def new_func(*args, **kwargs):
|
||||
warnings.simplefilter("always", EditorialDeprecatedWarning)
|
||||
warnings.warn(
|
||||
(
|
||||
"Call to deprecated function '{}'."
|
||||
" Function was moved to 'openpype.pipeline.editorial'."
|
||||
).format(func.__name__),
|
||||
category=EditorialDeprecatedWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
return func(*args, **kwargs)
|
||||
return new_func
|
||||
|
||||
|
||||
def trim_media_range(media_range, source_range):
|
||||
"""
|
||||
Trim input media range with clip source range.
|
||||
@editorial_deprecated
|
||||
def otio_range_to_frame_range(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import otio_range_to_frame_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)
|
||||
return otio_range_to_frame_range(*args, **kwargs)
|
||||
|
||||
|
||||
def range_from_frames(start, duration, fps):
|
||||
"""
|
||||
Returns otio time range.
|
||||
@editorial_deprecated
|
||||
def otio_range_with_handles(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import otio_range_with_handles
|
||||
|
||||
Args:
|
||||
start (int): frame start
|
||||
duration (int): frame duration
|
||||
fps (float): frame range
|
||||
|
||||
Returns:
|
||||
otio._ot._ot.TimeRange: created range
|
||||
|
||||
"""
|
||||
return _ot.TimeRange(
|
||||
_ot.RationalTime(start, fps),
|
||||
_ot.RationalTime(duration, fps)
|
||||
)
|
||||
return otio_range_with_handles(*args, **kwargs)
|
||||
|
||||
|
||||
def frames_to_secons(frames, framerate):
|
||||
"""
|
||||
Returning secons.
|
||||
@editorial_deprecated
|
||||
def is_overlapping_otio_ranges(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import is_overlapping_otio_ranges
|
||||
|
||||
Args:
|
||||
frames (int): frame
|
||||
framerate (float): frame rate
|
||||
|
||||
Returns:
|
||||
float: second value
|
||||
|
||||
"""
|
||||
rt = _ot.from_frames(frames, framerate)
|
||||
return _ot.to_seconds(rt)
|
||||
return is_overlapping_otio_ranges(*args, **kwargs)
|
||||
|
||||
|
||||
def frames_to_timecode(frames, framerate):
|
||||
rt = _ot.from_frames(frames, framerate)
|
||||
return _ot.to_timecode(rt)
|
||||
@editorial_deprecated
|
||||
def convert_to_padded_path(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import convert_to_padded_path
|
||||
|
||||
return convert_to_padded_path(*args, **kwargs)
|
||||
|
||||
|
||||
def make_sequence_collection(path, otio_range, metadata):
|
||||
"""
|
||||
Make collection from path otio range and otio metadata.
|
||||
@editorial_deprecated
|
||||
def trim_media_range(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import trim_media_range
|
||||
|
||||
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)])
|
||||
return dir_path, collection
|
||||
return trim_media_range(*args, **kwargs)
|
||||
|
||||
|
||||
def _sequence_resize(source, length):
|
||||
step = float(len(source) - 1) / (length - 1)
|
||||
for i in range(length):
|
||||
low, ratio = divmod(i * step, 1)
|
||||
high = low + 1 if ratio > 0 else low
|
||||
yield (1 - ratio) * source[int(low)] + ratio * source[int(high)]
|
||||
@editorial_deprecated
|
||||
def range_from_frames(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import range_from_frames
|
||||
|
||||
return range_from_frames(*args, **kwargs)
|
||||
|
||||
|
||||
def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
|
||||
source_range = otio_clip.source_range
|
||||
available_range = otio_clip.available_range()
|
||||
media_in = available_range.start_time.value
|
||||
media_out = available_range.end_time_inclusive().value
|
||||
@editorial_deprecated
|
||||
def frames_to_secons(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import frames_to_seconds
|
||||
|
||||
# modifiers
|
||||
time_scalar = 1.
|
||||
offset_in = 0
|
||||
offset_out = 0
|
||||
time_warp_nodes = []
|
||||
return frames_to_seconds(*args, **kwargs)
|
||||
|
||||
# Check for speed effects and adjust playback speed accordingly
|
||||
for effect in otio_clip.effects:
|
||||
if isinstance(effect, otio.schema.LinearTimeWarp):
|
||||
time_scalar = effect.time_scalar
|
||||
|
||||
elif isinstance(effect, otio.schema.FreezeFrame):
|
||||
# For freeze frame, playback speed must be set after range
|
||||
time_scalar = 0.
|
||||
@editorial_deprecated
|
||||
def frames_to_timecode(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import frames_to_timecode
|
||||
|
||||
elif isinstance(effect, otio.schema.TimeEffect):
|
||||
# For freeze frame, playback speed must be set after range
|
||||
name = effect.name
|
||||
effect_name = effect.effect_name
|
||||
if "TimeWarp" not in effect_name:
|
||||
continue
|
||||
metadata = effect.metadata
|
||||
lookup = metadata.get("lookup")
|
||||
if not lookup:
|
||||
continue
|
||||
return frames_to_timecode(*args, **kwargs)
|
||||
|
||||
# time warp node
|
||||
tw_node = {
|
||||
"Class": "TimeWarp",
|
||||
"name": name
|
||||
}
|
||||
tw_node.update(metadata)
|
||||
tw_node["lookup"] = list(lookup)
|
||||
|
||||
# get first and last frame offsets
|
||||
offset_in += lookup[0]
|
||||
offset_out += lookup[-1]
|
||||
@editorial_deprecated
|
||||
def make_sequence_collection(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import make_sequence_collection
|
||||
|
||||
# add to timewarp nodes
|
||||
time_warp_nodes.append(tw_node)
|
||||
return make_sequence_collection(*args, **kwargs)
|
||||
|
||||
# multiply by time scalar
|
||||
offset_in *= time_scalar
|
||||
offset_out *= time_scalar
|
||||
|
||||
# filip offset if reversed speed
|
||||
if time_scalar < 0:
|
||||
_offset_in = offset_out
|
||||
_offset_out = offset_in
|
||||
offset_in = _offset_in
|
||||
offset_out = _offset_out
|
||||
@editorial_deprecated
|
||||
def get_media_range_with_retimes(*args, **kwargs):
|
||||
from openpype.pipeline.editorial import get_media_range_with_retimes
|
||||
|
||||
# scale handles
|
||||
handle_start *= abs(time_scalar)
|
||||
handle_end *= abs(time_scalar)
|
||||
|
||||
# filip handles if reversed speed
|
||||
if time_scalar < 0:
|
||||
_handle_start = handle_end
|
||||
_handle_end = handle_start
|
||||
handle_start = _handle_start
|
||||
handle_end = _handle_end
|
||||
|
||||
source_in = source_range.start_time.value
|
||||
|
||||
media_in_trimmed = (
|
||||
media_in + source_in + offset_in)
|
||||
media_out_trimmed = (
|
||||
media_in + source_in + (
|
||||
((source_range.duration.value - 1) * abs(
|
||||
time_scalar)) + offset_out))
|
||||
|
||||
# calculate available handles
|
||||
if (media_in_trimmed - media_in) < handle_start:
|
||||
handle_start = (media_in_trimmed - media_in)
|
||||
if (media_out - media_out_trimmed) < handle_end:
|
||||
handle_end = (media_out - media_out_trimmed)
|
||||
|
||||
# create version data
|
||||
version_data = {
|
||||
"versionData": {
|
||||
"retime": True,
|
||||
"speed": time_scalar,
|
||||
"timewarps": time_warp_nodes,
|
||||
"handleStart": round(handle_start),
|
||||
"handleEnd": round(handle_end)
|
||||
}
|
||||
}
|
||||
|
||||
returning_dict = {
|
||||
"mediaIn": media_in_trimmed,
|
||||
"mediaOut": media_out_trimmed,
|
||||
"handleStart": round(handle_start),
|
||||
"handleEnd": round(handle_end)
|
||||
}
|
||||
|
||||
# add version data only if retime
|
||||
if time_warp_nodes or time_scalar != 1.:
|
||||
returning_dict.update(version_data)
|
||||
|
||||
return returning_dict
|
||||
return get_media_range_with_retimes(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
import importlib
|
||||
from .log import PypeLogger as Logger
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
def discover_host_vendor_module(module_name):
|
||||
host = os.environ["AVALON_APP"]
|
||||
pype_root = os.environ["OPENPYPE_REPOS_ROOT"]
|
||||
main_module = module_name.split(".")[0]
|
||||
module_path = os.path.join(
|
||||
pype_root, "hosts", host, "vendor", main_module)
|
||||
|
||||
log.debug(
|
||||
"Importing module from host vendor path: `{}`".format(module_path))
|
||||
|
||||
if not os.path.exists(module_path):
|
||||
log.warning(
|
||||
"Path not existing: `{}`".format(module_path))
|
||||
return None
|
||||
|
||||
sys.path.insert(1, module_path)
|
||||
return importlib.import_module(module_name)
|
||||
282
openpype/pipeline/editorial.py
Normal file
282
openpype/pipeline/editorial.py
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
import os
|
||||
import re
|
||||
import clique
|
||||
|
||||
import opentimelineio as otio
|
||||
from opentimelineio import opentime as _ot
|
||||
|
||||
|
||||
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)
|
||||
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: created range
|
||||
|
||||
"""
|
||||
return _ot.TimeRange(
|
||||
_ot.RationalTime(start, fps),
|
||||
_ot.RationalTime(duration, fps)
|
||||
)
|
||||
|
||||
|
||||
def frames_to_seconds(frames, framerate):
|
||||
"""
|
||||
Returning seconds.
|
||||
|
||||
Args:
|
||||
frames (int): frame
|
||||
framerate (float): 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)])
|
||||
return dir_path, collection
|
||||
|
||||
|
||||
def _sequence_resize(source, length):
|
||||
step = float(len(source) - 1) / (length - 1)
|
||||
for i in range(length):
|
||||
low, ratio = divmod(i * step, 1)
|
||||
high = low + 1 if ratio > 0 else low
|
||||
yield (1 - ratio) * source[int(low)] + ratio * source[int(high)]
|
||||
|
||||
|
||||
def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
|
||||
source_range = otio_clip.source_range
|
||||
available_range = otio_clip.available_range()
|
||||
media_in = available_range.start_time.value
|
||||
media_out = available_range.end_time_inclusive().value
|
||||
|
||||
# modifiers
|
||||
time_scalar = 1.
|
||||
offset_in = 0
|
||||
offset_out = 0
|
||||
time_warp_nodes = []
|
||||
|
||||
# Check for speed effects and adjust playback speed accordingly
|
||||
for effect in otio_clip.effects:
|
||||
if isinstance(effect, otio.schema.LinearTimeWarp):
|
||||
time_scalar = effect.time_scalar
|
||||
|
||||
elif isinstance(effect, otio.schema.FreezeFrame):
|
||||
# For freeze frame, playback speed must be set after range
|
||||
time_scalar = 0.
|
||||
|
||||
elif isinstance(effect, otio.schema.TimeEffect):
|
||||
# For freeze frame, playback speed must be set after range
|
||||
name = effect.name
|
||||
effect_name = effect.effect_name
|
||||
if "TimeWarp" not in effect_name:
|
||||
continue
|
||||
metadata = effect.metadata
|
||||
lookup = metadata.get("lookup")
|
||||
if not lookup:
|
||||
continue
|
||||
|
||||
# time warp node
|
||||
tw_node = {
|
||||
"Class": "TimeWarp",
|
||||
"name": name
|
||||
}
|
||||
tw_node.update(metadata)
|
||||
tw_node["lookup"] = list(lookup)
|
||||
|
||||
# get first and last frame offsets
|
||||
offset_in += lookup[0]
|
||||
offset_out += lookup[-1]
|
||||
|
||||
# add to timewarp nodes
|
||||
time_warp_nodes.append(tw_node)
|
||||
|
||||
# multiply by time scalar
|
||||
offset_in *= time_scalar
|
||||
offset_out *= time_scalar
|
||||
|
||||
# filip offset if reversed speed
|
||||
if time_scalar < 0:
|
||||
_offset_in = offset_out
|
||||
_offset_out = offset_in
|
||||
offset_in = _offset_in
|
||||
offset_out = _offset_out
|
||||
|
||||
# scale handles
|
||||
handle_start *= abs(time_scalar)
|
||||
handle_end *= abs(time_scalar)
|
||||
|
||||
# filip handles if reversed speed
|
||||
if time_scalar < 0:
|
||||
_handle_start = handle_end
|
||||
_handle_end = handle_start
|
||||
handle_start = _handle_start
|
||||
handle_end = _handle_end
|
||||
|
||||
source_in = source_range.start_time.value
|
||||
|
||||
media_in_trimmed = (
|
||||
media_in + source_in + offset_in)
|
||||
media_out_trimmed = (
|
||||
media_in + source_in + (
|
||||
((source_range.duration.value - 1) * abs(
|
||||
time_scalar)) + offset_out))
|
||||
|
||||
# calculate available handles
|
||||
if (media_in_trimmed - media_in) < handle_start:
|
||||
handle_start = (media_in_trimmed - media_in)
|
||||
if (media_out - media_out_trimmed) < handle_end:
|
||||
handle_end = (media_out - media_out_trimmed)
|
||||
|
||||
# create version data
|
||||
version_data = {
|
||||
"versionData": {
|
||||
"retime": True,
|
||||
"speed": time_scalar,
|
||||
"timewarps": time_warp_nodes,
|
||||
"handleStart": round(handle_start),
|
||||
"handleEnd": round(handle_end)
|
||||
}
|
||||
}
|
||||
|
||||
returning_dict = {
|
||||
"mediaIn": media_in_trimmed,
|
||||
"mediaOut": media_out_trimmed,
|
||||
"handleStart": round(handle_start),
|
||||
"handleEnd": round(handle_end)
|
||||
}
|
||||
|
||||
# add version data only if retime
|
||||
if time_warp_nodes or time_scalar != 1.:
|
||||
returning_dict.update(version_data)
|
||||
|
||||
return returning_dict
|
||||
|
|
@ -8,8 +8,11 @@ Requires:
|
|||
# import os
|
||||
import opentimelineio as otio
|
||||
import pyblish.api
|
||||
import openpype.lib
|
||||
from pprint import pformat
|
||||
from openpype.pipeline.editorial import (
|
||||
otio_range_to_frame_range,
|
||||
otio_range_with_handles
|
||||
)
|
||||
|
||||
|
||||
class CollectOtioFrameRanges(pyblish.api.InstancePlugin):
|
||||
|
|
@ -31,9 +34,9 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin):
|
|||
otio_tl_range = otio_clip.range_in_parent()
|
||||
otio_src_range = otio_clip.source_range
|
||||
otio_avalable_range = otio_clip.available_range()
|
||||
otio_tl_range_handles = openpype.lib.otio_range_with_handles(
|
||||
otio_tl_range_handles = otio_range_with_handles(
|
||||
otio_tl_range, instance)
|
||||
otio_src_range_handles = openpype.lib.otio_range_with_handles(
|
||||
otio_src_range_handles = otio_range_with_handles(
|
||||
otio_src_range, instance)
|
||||
|
||||
# get source avalable start frame
|
||||
|
|
@ -42,7 +45,7 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin):
|
|||
otio_avalable_range.start_time.rate)
|
||||
|
||||
# convert to frames
|
||||
range_convert = openpype.lib.otio_range_to_frame_range
|
||||
range_convert = otio_range_to_frame_range
|
||||
tl_start, tl_end = range_convert(otio_tl_range)
|
||||
tl_start_h, tl_end_h = range_convert(otio_tl_range_handles)
|
||||
src_start, src_end = range_convert(otio_src_range)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,11 @@ import os
|
|||
import clique
|
||||
import opentimelineio as otio
|
||||
import pyblish.api
|
||||
import openpype.lib as oplib
|
||||
from openpype.pipeline.editorial import (
|
||||
get_media_range_with_retimes,
|
||||
range_from_frames,
|
||||
make_sequence_collection
|
||||
)
|
||||
|
||||
|
||||
class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
|
||||
|
|
@ -42,7 +46,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
|
|||
available_duration = otio_avalable_range.duration.value
|
||||
|
||||
# get available range trimmed with processed retimes
|
||||
retimed_attributes = oplib.get_media_range_with_retimes(
|
||||
retimed_attributes = get_media_range_with_retimes(
|
||||
otio_clip, handle_start, handle_end)
|
||||
self.log.debug(
|
||||
">> retimed_attributes: {}".format(retimed_attributes))
|
||||
|
|
@ -64,7 +68,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
|
|||
a_frame_end_h = media_out + handle_end
|
||||
|
||||
# create trimmed otio time range
|
||||
trimmed_media_range_h = oplib.range_from_frames(
|
||||
trimmed_media_range_h = range_from_frames(
|
||||
a_frame_start_h, (a_frame_end_h - a_frame_start_h) + 1,
|
||||
media_fps
|
||||
)
|
||||
|
|
@ -144,7 +148,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
|
|||
# in case it is file sequence but not new OTIO schema
|
||||
# `ImageSequenceReference`
|
||||
path = media_ref.target_url
|
||||
collection_data = oplib.make_sequence_collection(
|
||||
collection_data = make_sequence_collection(
|
||||
path, trimmed_media_range_h, metadata)
|
||||
self.staging_dir, collection = collection_data
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ import clique
|
|||
import opentimelineio as otio
|
||||
from pyblish import api
|
||||
import openpype
|
||||
from openpype.pipeline.editorial import (
|
||||
otio_range_to_frame_range,
|
||||
trim_media_range,
|
||||
range_from_frames,
|
||||
frames_to_seconds,
|
||||
make_sequence_collection
|
||||
)
|
||||
|
||||
|
||||
class ExtractOTIOReview(openpype.api.Extractor):
|
||||
|
|
@ -161,7 +168,7 @@ class ExtractOTIOReview(openpype.api.Extractor):
|
|||
dirname = media_ref.target_url_base
|
||||
head = media_ref.name_prefix
|
||||
tail = media_ref.name_suffix
|
||||
first, last = openpype.lib.otio_range_to_frame_range(
|
||||
first, last = otio_range_to_frame_range(
|
||||
available_range)
|
||||
collection = clique.Collection(
|
||||
head=head,
|
||||
|
|
@ -180,7 +187,7 @@ class ExtractOTIOReview(openpype.api.Extractor):
|
|||
# in case it is file sequence but not new OTIO schema
|
||||
# `ImageSequenceReference`
|
||||
path = media_ref.target_url
|
||||
collection_data = openpype.lib.make_sequence_collection(
|
||||
collection_data = make_sequence_collection(
|
||||
path, available_range, metadata)
|
||||
dir_path, collection = collection_data
|
||||
|
||||
|
|
@ -305,8 +312,8 @@ class ExtractOTIOReview(openpype.api.Extractor):
|
|||
duration = avl_durtation
|
||||
|
||||
# return correct trimmed range
|
||||
return openpype.lib.trim_media_range(
|
||||
avl_range, openpype.lib.range_from_frames(start, duration, fps)
|
||||
return trim_media_range(
|
||||
avl_range, range_from_frames(start, duration, fps)
|
||||
)
|
||||
|
||||
def _render_seqment(self, sequence=None,
|
||||
|
|
@ -357,8 +364,8 @@ class ExtractOTIOReview(openpype.api.Extractor):
|
|||
frame_start = otio_range.start_time.value
|
||||
input_fps = otio_range.start_time.rate
|
||||
frame_duration = otio_range.duration.value
|
||||
sec_start = openpype.lib.frames_to_secons(frame_start, input_fps)
|
||||
sec_duration = openpype.lib.frames_to_secons(
|
||||
sec_start = frames_to_seconds(frame_start, input_fps)
|
||||
sec_duration = frames_to_seconds(
|
||||
frame_duration, input_fps
|
||||
)
|
||||
|
||||
|
|
@ -370,8 +377,7 @@ class ExtractOTIOReview(openpype.api.Extractor):
|
|||
])
|
||||
|
||||
elif gap:
|
||||
sec_duration = openpype.lib.frames_to_secons(
|
||||
gap, self.actual_fps)
|
||||
sec_duration = frames_to_seconds(gap, self.actual_fps)
|
||||
|
||||
# form command for rendering gap files
|
||||
command.extend([
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import os
|
|||
from pyblish import api
|
||||
import openpype
|
||||
from copy import deepcopy
|
||||
from openpype.pipeline.editorial import frames_to_seconds
|
||||
|
||||
|
||||
class ExtractOTIOTrimmingVideo(openpype.api.Extractor):
|
||||
|
|
@ -81,8 +82,8 @@ class ExtractOTIOTrimmingVideo(openpype.api.Extractor):
|
|||
frame_start = otio_range.start_time.value
|
||||
input_fps = otio_range.start_time.rate
|
||||
frame_duration = otio_range.duration.value - 1
|
||||
sec_start = openpype.lib.frames_to_secons(frame_start, input_fps)
|
||||
sec_duration = openpype.lib.frames_to_secons(frame_duration, input_fps)
|
||||
sec_start = frames_to_seconds(frame_start, input_fps)
|
||||
sec_duration = frames_to_seconds(frame_duration, input_fps)
|
||||
|
||||
# form command for rendering gap files
|
||||
command.extend([
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue