feat(nukestudio): adding otio plugin

This commit is contained in:
Jakub Jezek 2019-05-11 18:17:39 +02:00
parent 7237741033
commit 38c693a792
5 changed files with 955 additions and 0 deletions

View file

@ -0,0 +1,369 @@
# MIT License
#
# Copyright (c) 2018 Daniel Flehner Heen (Storm Studios)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import re
import hiero.core
from hiero.core import util
import opentimelineio as otio
marker_color_map = {
"magenta": otio.schema.MarkerColor.MAGENTA,
"red": otio.schema.MarkerColor.RED,
"yellow": otio.schema.MarkerColor.YELLOW,
"green": otio.schema.MarkerColor.GREEN,
"cyan": otio.schema.MarkerColor.CYAN,
"blue": otio.schema.MarkerColor.BLUE,
}
class OTIOExportTask(hiero.core.TaskBase):
def __init__(self, initDict):
"""Initialize"""
hiero.core.TaskBase.__init__(self, initDict)
def name(self):
return str(type(self))
def get_rate(self, item):
num, den = item.framerate().toRational()
rate = float(num) / float(den)
if rate.is_integer():
return rate
return round(rate, 2)
def get_clip_ranges(self, trackitem):
# Is clip an audio file? Use sequence frame rate
if not trackitem.source().mediaSource().hasVideo():
rate_item = trackitem.sequence()
else:
rate_item = trackitem.source()
source_rate = self.get_rate(rate_item)
# Reversed video/audio
if trackitem.playbackSpeed() < 0:
start = trackitem.sourceOut()
else:
start = trackitem.sourceIn()
source_start_time = otio.opentime.RationalTime(
start,
source_rate
)
source_duration = otio.opentime.RationalTime(
trackitem.duration(),
source_rate
)
source_range = otio.opentime.TimeRange(
start_time=source_start_time,
duration=source_duration
)
available_range = None
hiero_clip = trackitem.source()
if not hiero_clip.mediaSource().isOffline():
start_time = otio.opentime.RationalTime(
hiero_clip.mediaSource().startTime(),
source_rate
)
duration = otio.opentime.RationalTime(
hiero_clip.mediaSource().duration(),
source_rate
)
available_range = otio.opentime.TimeRange(
start_time=start_time,
duration=duration
)
return source_range, available_range
def add_gap(self, trackitem, otio_track, prev_out):
gap_length = trackitem.timelineIn() - prev_out
if prev_out != 0:
gap_length -= 1
rate = self.get_rate(trackitem.sequence())
gap = otio.opentime.TimeRange(
duration=otio.opentime.RationalTime(
gap_length,
rate
)
)
otio_gap = otio.schema.Gap(source_range=gap)
otio_track.append(otio_gap)
def get_marker_color(self, tag):
icon = tag.icon()
pat = 'icons:Tag(?P<color>\w+)\.\w+'
res = re.search(pat, icon)
if res:
color = res.groupdict().get('color')
if color.lower() in marker_color_map:
return marker_color_map[color.lower()]
return otio.schema.MarkerColor.RED
def add_markers(self, hiero_item, otio_item):
for tag in hiero_item.tags():
if not tag.visible():
continue
if tag.name() == 'Copy':
# Hiero adds this tag to a lot of clips
continue
frame_rate = self.get_rate(hiero_item)
marked_range = otio.opentime.TimeRange(
start_time=otio.opentime.RationalTime(
tag.inTime(),
frame_rate
),
duration=otio.opentime.RationalTime(
int(tag.metadata().dict().get('tag.length', '0')),
frame_rate
)
)
marker = otio.schema.Marker(
name=tag.name(),
color=self.get_marker_color(tag),
marked_range=marked_range,
metadata={
'Hiero': tag.metadata().dict()
}
)
otio_item.markers.append(marker)
def add_clip(self, trackitem, otio_track, itemindex):
hiero_clip = trackitem.source()
# Add Gap if needed
prev_item = (
itemindex and trackitem.parent().items()[itemindex - 1] or
trackitem
)
if prev_item == trackitem and trackitem.timelineIn() > 0:
self.add_gap(trackitem, otio_track, 0)
elif (
prev_item != trackitem and
prev_item.timelineOut() != trackitem.timelineIn()
):
self.add_gap(trackitem, otio_track, prev_item.timelineOut())
# Create Clip
source_range, available_range = self.get_clip_ranges(trackitem)
otio_clip = otio.schema.Clip()
otio_clip.name = trackitem.name()
otio_clip.source_range = source_range
# Add media reference
media_reference = otio.schema.MissingReference()
if not hiero_clip.mediaSource().isOffline():
source = hiero_clip.mediaSource()
media_reference = otio.schema.ExternalReference()
media_reference.available_range = available_range
path, name = os.path.split(source.fileinfos()[0].filename())
media_reference.target_url = os.path.join(path, name)
media_reference.name = name
otio_clip.media_reference = media_reference
# Add Time Effects
playbackspeed = trackitem.playbackSpeed()
if playbackspeed != 1:
if playbackspeed == 0:
time_effect = otio.schema.FreezeFrame()
else:
time_effect = otio.schema.LinearTimeWarp(
time_scalar=playbackspeed
)
otio_clip.effects.append(time_effect)
# Add tags as markers
if self._preset.properties()["includeTags"]:
self.add_markers(trackitem.source(), otio_clip)
otio_track.append(otio_clip)
# Add Transition if needed
if trackitem.inTransition() or trackitem.outTransition():
self.add_transition(trackitem, otio_track)
def add_transition(self, trackitem, otio_track):
transitions = []
if trackitem.inTransition():
if trackitem.inTransition().alignment().name == 'kFadeIn':
transitions.append(trackitem.inTransition())
if trackitem.outTransition():
transitions.append(trackitem.outTransition())
for transition in transitions:
alignment = transition.alignment().name
if alignment == 'kFadeIn':
in_offset_frames = 0
out_offset_frames = (
transition.timelineOut() - transition.timelineIn()
) + 1
elif alignment == 'kFadeOut':
in_offset_frames = (
trackitem.timelineOut() - transition.timelineIn()
) + 1
out_offset_frames = 0
elif alignment == 'kDissolve':
in_offset_frames = (
transition.inTrackItem().timelineOut() -
transition.timelineIn()
)
out_offset_frames = (
transition.timelineOut() -
transition.outTrackItem().timelineIn()
)
else:
# kUnknown transition is ignored
continue
rate = trackitem.source().framerate().toFloat()
in_time = otio.opentime.RationalTime(in_offset_frames, rate)
out_time = otio.opentime.RationalTime(out_offset_frames, rate)
otio_transition = otio.schema.Transition(
name=alignment, # Consider placing Hiero name in metadata
transition_type=otio.schema.TransitionTypes.SMPTE_Dissolve,
in_offset=in_time,
out_offset=out_time,
metadata={}
)
if alignment == 'kFadeIn':
otio_track.insert(-2, otio_transition)
else:
otio_track.append(otio_transition)
def add_tracks(self):
for track in self._sequence.items():
if isinstance(track, hiero.core.AudioTrack):
kind = otio.schema.TrackKind.Audio
else:
kind = otio.schema.TrackKind.Video
otio_track = otio.schema.Track(kind=kind)
otio_track.name = track.name()
for itemindex, trackitem in enumerate(track):
if isinstance(trackitem.source(), hiero.core.Clip):
self.add_clip(trackitem, otio_track, itemindex)
self.otio_timeline.tracks.append(otio_track)
# Add tags as markers
if self._preset.properties()["includeTags"]:
self.add_markers(self._sequence, self.otio_timeline.tracks)
def create_OTIO(self):
self.otio_timeline = otio.schema.Timeline()
self.otio_timeline.name = self._sequence.name()
self.add_tracks()
def startTask(self):
self.create_OTIO()
def taskStep(self):
return False
def finishTask(self):
try:
exportPath = self.resolvedExportPath()
# Check file extension
if not exportPath.lower().endswith(".otio"):
exportPath += ".otio"
# check export root exists
dirname = os.path.dirname(exportPath)
util.filesystem.makeDirs(dirname)
# write otio file
otio.adapters.write_to_file(self.otio_timeline, exportPath)
# Catch all exceptions and log error
except Exception as e:
self.setError("failed to write file {f}\n{e}".format(
f=exportPath,
e=e)
)
hiero.core.TaskBase.finishTask(self)
def forcedAbort(self):
pass
class OTIOExportPreset(hiero.core.TaskPresetBase):
def __init__(self, name, properties):
"""Initialise presets to default values"""
hiero.core.TaskPresetBase.__init__(self, OTIOExportTask, name)
self.properties()["includeTags"] = True
self.properties().update(properties)
def supportedItems(self):
return hiero.core.TaskPresetBase.kSequence
def addCustomResolveEntries(self, resolver):
resolver.addResolver(
"{ext}",
"Extension of the file to be output",
lambda keyword, task: "otio"
)
def supportsAudio(self):
return True
hiero.core.taskRegistry.registerTask(OTIOExportPreset, OTIOExportTask)

View file

@ -0,0 +1,65 @@
import hiero.ui
import OTIOExportTask
try:
# Hiero >= 11.x
from PySide2 import QtCore
from PySide2.QtWidgets import QCheckBox
from hiero.ui.FnTaskUIFormLayout import TaskUIFormLayout as FormLayout
except ImportError:
# Hiero <= 10.x
from PySide import QtCore # lint:ok
from PySide.QtGui import QCheckBox, QFormLayout # lint:ok
FormLayout = QFormLayout # lint:ok
class OTIOExportUI(hiero.ui.TaskUIBase):
def __init__(self, preset):
"""Initialize"""
hiero.ui.TaskUIBase.__init__(
self,
OTIOExportTask.OTIOExportTask,
preset,
"OTIO Exporter"
)
def includeMarkersCheckboxChanged(self, state):
# Slot to handle change of checkbox state
self._preset.properties()["includeTags"] = state == QtCore.Qt.Checked
def populateUI(self, widget, exportTemplate):
layout = widget.layout()
formLayout = FormLayout()
# Hiero ~= 10.0v4
if layout is None:
layout = formLayout
widget.setLayout(layout)
else:
layout.addLayout(formLayout)
# Checkboxes for whether the OTIO should contain markers or not
self.includeMarkersCheckbox = QCheckBox()
self.includeMarkersCheckbox.setToolTip(
"Enable to include Tags as markers in the exported OTIO file."
)
self.includeMarkersCheckbox.setCheckState(QtCore.Qt.Unchecked)
if self._preset.properties()["includeTags"]:
self.includeMarkersCheckbox.setCheckState(QtCore.Qt.Checked)
self.includeMarkersCheckbox.stateChanged.connect(
self.includeMarkersCheckboxChanged
)
# Add Checkbox to layout
formLayout.addRow("Include Tags:", self.includeMarkersCheckbox)
hiero.ui.taskUIRegistry.registerTaskUI(
OTIOExportTask.OTIOExportPreset,
OTIOExportUI
)

View file

@ -0,0 +1,29 @@
# MIT License
#
# Copyright (c) 2018 Daniel Flehner Heen (Storm Studios)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from OTIOExportTask import OTIOExportTask
from OTIOExportUI import OTIOExportUI
__all__ = [
'OTIOExportTask',
'OTIOExportUI'
]

View file

@ -0,0 +1,435 @@
# MIT License
#
# Copyright (c) 2018 Daniel Flehner Heen (Storm Studios)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sys
import hiero.core
import hiero.ui
try:
from urllib import unquote
except ImportError:
from urllib.parse import unquote # lint:ok
import opentimelineio as otio
def get_transition_type(otio_item, otio_track):
_in, _out = otio_track.neighbors_of(otio_item)
if isinstance(_in, otio.schema.Gap):
_in = None
if isinstance(_out, otio.schema.Gap):
_out = None
if _in and _out:
return 'dissolve'
elif _in and not _out:
return 'fade_out'
elif not _in and _out:
return 'fade_in'
else:
return 'unknown'
def find_trackitem(name, hiero_track):
for item in hiero_track.items():
if item.name() == name:
return item
return None
def get_neighboring_trackitems(otio_item, otio_track, hiero_track):
_in, _out = otio_track.neighbors_of(otio_item)
trackitem_in = None
trackitem_out = None
if _in:
trackitem_in = find_trackitem(_in.name, hiero_track)
if _out:
trackitem_out = find_trackitem(_out.name, hiero_track)
return trackitem_in, trackitem_out
def apply_transition(otio_track, otio_item, track):
# Figure out type of transition
transition_type = get_transition_type(otio_item, otio_track)
# Figure out track kind for getattr below
if isinstance(track, hiero.core.VideoTrack):
kind = ''
else:
kind = 'Audio'
try:
# Gather TrackItems involved in trasition
item_in, item_out = get_neighboring_trackitems(
otio_item,
otio_track,
track
)
# Create transition object
if transition_type == 'dissolve':
transition_func = getattr(
hiero.core.Transition,
'create{kind}DissolveTransition'.format(kind=kind)
)
transition = transition_func(
item_in,
item_out,
otio_item.in_offset.value,
otio_item.out_offset.value
)
elif transition_type == 'fade_in':
transition_func = getattr(
hiero.core.Transition,
'create{kind}FadeInTransition'.format(kind=kind)
)
transition = transition_func(
item_out,
otio_item.out_offset.value
)
elif transition_type == 'fade_out':
transition_func = getattr(
hiero.core.Transition,
'create{kind}FadeOutTransition'.format(kind=kind)
)
transition = transition_func(
item_in,
otio_item.in_offset.value
)
else:
# Unknown transition
return
# Apply transition to track
track.addTransition(transition)
except Exception, e:
sys.stderr.write(
'Unable to apply transition "{t}": "{e}"\n'.format(
t=otio_item,
e=e
)
)
def prep_url(url_in):
url = unquote(url_in)
if url.startswith('file://localhost/'):
return url.replace('file://localhost/', '')
url = '{url}'.format(
sep=url.startswith(os.sep) and '' or os.sep,
url=url.startswith(os.sep) and url[1:] or url
)
return url
def create_offline_mediasource(otio_clip, path=None):
hiero_rate = hiero.core.TimeBase(
otio_clip.source_range.start_time.rate
)
if isinstance(otio_clip.media_reference, otio.schema.ExternalReference):
source_range = otio_clip.available_range()
else:
source_range = otio_clip.source_range
if path is None:
path = otio_clip.name
media = hiero.core.MediaSource.createOfflineVideoMediaSource(
prep_url(path),
source_range.start_time.value,
source_range.duration.value,
hiero_rate,
source_range.start_time.value
)
return media
def load_otio(otio_file):
otio_timeline = otio.adapters.read_from_file(otio_file)
build_sequence(otio_timeline)
marker_color_map = {
"PINK": "Magenta",
"RED": "Red",
"ORANGE": "Yellow",
"YELLOW": "Yellow",
"GREEN": "Green",
"CYAN": "Cyan",
"BLUE": "Blue",
"PURPLE": "Magenta",
"MAGENTA": "Magenta",
"BLACK": "Blue",
"WHITE": "Green"
}
def get_tag(tagname, tagsbin):
for tag in tagsbin.items():
if tag.name() == tagname:
return tag
if isinstance(tag, hiero.core.Bin):
tag = get_tag(tagname, tag)
if tag is not None:
return tag
return None
def add_metadata(metadata, hiero_item):
for key, value in metadata.items():
if isinstance(value, dict):
add_metadata(value, hiero_item)
continue
if value is not None:
if not key.startswith('tag.'):
key = 'tag.' + key
hiero_item.metadata().setValue(key, str(value))
def add_markers(otio_item, hiero_item, tagsbin):
if isinstance(otio_item, (otio.schema.Stack, otio.schema.Clip)):
markers = otio_item.markers
elif isinstance(otio_item, otio.schema.Timeline):
markers = otio_item.tracks.markers
else:
markers = []
for marker in markers:
marker_color = marker.color
_tag = get_tag(marker.name, tagsbin)
if _tag is None:
_tag = get_tag(marker_color_map[marker_color], tagsbin)
if _tag is None:
_tag = hiero.core.Tag(marker_color_map[marker.color])
start = marker.marked_range.start_time.value
end = (
marker.marked_range.start_time.value +
marker.marked_range.duration.value
)
tag = hiero_item.addTagToRange(_tag, start, end)
tag.setName(marker.name or marker_color_map[marker_color])
# Add metadata
add_metadata(marker.metadata, tag)
def create_track(otio_track, tracknum, track_kind):
# Add track kind when dealing with nested stacks
if isinstance(otio_track, otio.schema.Stack):
otio_track.kind = track_kind
# Create a Track
if otio_track.kind == otio.schema.TrackKind.Video:
track = hiero.core.VideoTrack(
otio_track.name or 'Video{n}'.format(n=tracknum)
)
else:
track = hiero.core.AudioTrack(
otio_track.name or 'Audio{n}'.format(n=tracknum)
)
return track
def create_clip(otio_clip, tagsbin):
# Create MediaSource
otio_media = otio_clip.media_reference
if isinstance(otio_media, otio.schema.ExternalReference):
url = prep_url(otio_media.target_url)
media = hiero.core.MediaSource(url)
if media.isOffline():
media = create_offline_mediasource(otio_clip, url)
else:
media = create_offline_mediasource(otio_clip)
# Create Clip
clip = hiero.core.Clip(media)
# Add markers
add_markers(otio_clip, clip, tagsbin)
return clip
def create_trackitem(playhead, track, otio_clip, clip):
source_range = otio_clip.source_range
trackitem = track.createTrackItem(otio_clip.name)
trackitem.setPlaybackSpeed(source_range.start_time.rate)
trackitem.setSource(clip)
# Check for speed effects and adjust playback speed accordingly
for effect in otio_clip.effects:
if isinstance(effect, otio.schema.LinearTimeWarp):
trackitem.setPlaybackSpeed(
trackitem.playbackSpeed() *
effect.time_scalar
)
# If reverse playback speed swap source in and out
if trackitem.playbackSpeed() < 0:
source_out = source_range.start_time.value
source_in = (
source_range.start_time.value +
source_range.duration.value
) - 1
timeline_in = playhead + source_out
timeline_out = (
timeline_in +
source_range.duration.value
) - 1
else:
# Normal playback speed
source_in = source_range.start_time.value
source_out = (
source_range.start_time.value +
source_range.duration.value
) - 1
timeline_in = playhead
timeline_out = (
timeline_in +
source_range.duration.value
) - 1
# Set source and timeline in/out points
trackitem.setSourceIn(source_in)
trackitem.setSourceOut(source_out)
trackitem.setTimelineIn(timeline_in)
trackitem.setTimelineOut(timeline_out)
return trackitem
def build_sequence(otio_timeline, project=None, track_kind=None):
if project is None:
# TODO: Find a proper way for active project
project = hiero.core.projects(hiero.core.Project.kUserProjects)[-1]
# Create a Sequence
sequence = hiero.core.Sequence(otio_timeline.name or 'OTIOSequence')
# Create a Bin to hold clips
projectbin = project.clipsBin()
projectbin.addItem(hiero.core.BinItem(sequence))
sequencebin = hiero.core.Bin(sequence.name())
projectbin.addItem(sequencebin)
# Get tagsBin
tagsbin = hiero.core.project("Tag Presets").tagsBin()
# Add timeline markers
add_markers(otio_timeline, sequence, tagsbin)
# TODO: Set sequence settings from otio timeline if available
if isinstance(otio_timeline, otio.schema.Timeline):
tracks = otio_timeline.tracks
else:
# otio.schema.Stack
tracks = otio_timeline
for tracknum, otio_track in enumerate(tracks):
playhead = 0
_transitions = []
# Add track to sequence
track = create_track(otio_track, tracknum, track_kind)
sequence.addTrack(track)
# iterate over items in track
for itemnum, otio_clip in enumerate(otio_track):
if isinstance(otio_clip, otio.schema.Stack):
bar = hiero.ui.mainWindow().statusBar()
bar.showMessage(
'Nested sequences are created separately.',
timeout=3000
)
build_sequence(otio_clip, project, otio_track.kind)
elif isinstance(otio_clip, otio.schema.Clip):
# Create a Clip
clip = create_clip(otio_clip, tagsbin)
# Add Clip to a Bin
sequencebin.addItem(hiero.core.BinItem(clip))
# Create TrackItem
trackitem = create_trackitem(
playhead,
track,
otio_clip,
clip
)
# Add trackitem to track
track.addTrackItem(trackitem)
# Update playhead
playhead = trackitem.timelineOut() + 1
elif isinstance(otio_clip, otio.schema.Transition):
# Store transitions for when all clips in the track are created
_transitions.append((otio_track, otio_clip))
elif isinstance(otio_clip, otio.schema.Gap):
# Hiero has no fillers, slugs or blanks at the moment
playhead += otio_clip.source_range.duration.value
# Apply transitions we stored earlier now that all clips are present
for otio_track, otio_item in _transitions:
apply_transition(otio_track, otio_item, track)

View file

@ -0,0 +1,57 @@
# MIT License
#
# Copyright (c) 2018 Daniel Flehner Heen (Storm Studios)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import hiero.ui
import hiero.core
from otioimporter.OTIOImport import load_otio
def OTIO_menu_action(event):
otio_action = hiero.ui.createMenuAction(
'Import OTIO',
open_otio_file,
icon=None
)
hiero.ui.registerAction(otio_action)
for action in event.menu.actions():
if action.text() == 'Import':
action.menu().addAction(otio_action)
break
def open_otio_file():
files = hiero.ui.openFileBrowser(
caption='Please select an OTIO file of choice',
pattern='*.otio',
requiredExtension='.otio'
)
for otio_file in files:
load_otio(otio_file)
# HieroPlayer is quite limited and can't create transitions etc.
if not hiero.core.isHieroPlayer():
hiero.core.events.registerInterest(
"kShowContextMenu/kBin",
OTIO_menu_action
)