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:
Jakub Trllo 2022-06-28 12:44:58 +02:00 committed by GitHub
commit c4bed47cd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 391 additions and 304 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View 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

View file

@ -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)

View file

@ -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

View file

@ -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([

View file

@ -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([