Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 80 additions & 16 deletions src/py-opentimelineio/opentimelineio/adapters/fcp_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ def media_reference_for_file_element(self, file_element, context):
name = _name_from_element(file_element)

# Get the full metadata
metadata_ignore_keys = {"duration", "name", "pathurl"}
metadata_ignore_keys = {"duration", "name", "pathurl", "mediaSource"}
md_dict = _xml_tree_to_dict(file_element, metadata_ignore_keys)
metadata_dict = {META_NAMESPACE: md_dict} if md_dict else None

Expand All @@ -865,6 +865,13 @@ def media_reference_for_file_element(self, file_element, context):
else:
path = None

# Determine the mediasource
mediasource_element = file_element.find("./mediaSource")
if mediasource_element is not None:
mediasource = mediasource_element.text
else:
mediasource = None

# Find the timing
timecode_element = file_element.find("./timecode")
if timecode_element is not None:
Expand All @@ -887,19 +894,26 @@ def media_reference_for_file_element(self, file_element, context):
else:
available_range = None

if path is None:
media_reference = schema.MissingReference(
if path is not None:
media_reference = schema.ExternalReference(
target_url=path,
available_range=available_range,
metadata=metadata_dict,
)
media_reference.name = name
elif mediasource is not None:
media_reference = schema.GeneratorReference(
name=name,
generator_kind=mediasource,
available_range=available_range,
metadata=metadata_dict,
)
else:
media_reference = schema.ExternalReference(
target_url=path,
media_reference = schema.MissingReference(
name=name,
available_range=available_range,
metadata=metadata_dict,
)
media_reference.name = name

return media_reference

Expand All @@ -912,10 +926,16 @@ def media_reference_for_effect_element(self, effect_element):
:return: An :class: `schema.GeneratorReference` instance.
"""
name = _name_from_element(effect_element)
md_dict = _xml_tree_to_dict(effect_element, {"name"})
md_dict = _xml_tree_to_dict(effect_element, {"name", "effectid"})

effectid_element = effect_element.find("./effectid")
generator_kind = (
effectid_element.text if effectid_element is not None else ""
)

return schema.GeneratorReference(
name=name,
generator_kind=generator_kind,
metadata=({META_NAMESPACE: md_dict} if md_dict else None)
)

Expand Down Expand Up @@ -1471,14 +1491,30 @@ def _build_file(media_reference, br_map):
file_e = _element_with_item_metadata("file", media_reference)

available_range = media_reference.available_range
url_path = _url_to_path(media_reference.target_url)

file_name = (
media_reference.name if media_reference.name
else os.path.basename(url_path)
# If the media reference is of one of the supported types, populate
# the appropriate source info element
if isinstance(media_reference, schema.ExternalReference):
_append_new_sub_element(
file_e, 'pathurl', text=media_reference.target_url
)
url_path = _url_to_path(media_reference.target_url)

fallback_file_name = (
media_reference.name if media_reference.name
else os.path.basename(url_path)
)
elif isinstance(media_reference, schema.GeneratorReference):
_append_new_sub_element(
file_e, 'mediaSource', text=media_reference.generator_kind
)
fallback_file_name = media_reference.generator_kind

_append_new_sub_element(
file_e,
'name',
text=(media_reference.name or fallback_file_name),
)
_append_new_sub_element(file_e, 'name', text=file_name)
_append_new_sub_element(file_e, 'pathurl', text=media_reference.target_url)

# timing info
file_e.append(_build_rate(available_range.start_time.rate))
Expand Down Expand Up @@ -1607,16 +1643,41 @@ def _build_clip_item_without_media(

@_backreference_build("clipitem")
def _build_clip_item(clip_item, timeline_range, transition_offsets, br_map):
# This is some wacky logic, but here's why:
# Pretty much any generator from Premiere just reports as being a clip that
# uses Slug as mediaSource rather than a pathurl (color matte seems to be
# the exception). I think this is becasue it is aiming to roundtrip effects
# with itself rather than try to make them backward compatable with FCP 7.
# This allows Premiere generators to still come in as slugs and still exist
# as placeholders for effects that may not have a true analog in FCP 7.
# Since OTIO does not yet interpret these generators into specific
# first-class schema objects (e.x. color matte, bars, etc.), the
# "artificial" mediaSources on clipitem and generatoritem both interpret as
# generator references. So, for the moment, to detect if likely have the
# metadata to make an fcp 7 style generatoritem we look for the effecttype
# field, if that is missing we write the generator using mediaSource in the
# Premiere Pro style.
# This adapter is currently built to effectively round-trip and let savvy
# users push the correct data into the metadata dictionary to drive
# behavior, but in the future when there are specific generator schema in
# otio we could correctly translate a first-class OTIO generator concept
# into an equivalent FCP 7 generatoritem or a Premiere Pro style overloaded
# clipitem.
is_generator = isinstance(
clip_item.media_reference, schema.GeneratorReference
)

tagname = "generatoritem" if is_generator else "clipitem"
media_ref_fcp_md = clip_item.media_reference.metadata.get('fcp_xml', {})
is_generatoritem = (
is_generator and 'effecttype' in media_ref_fcp_md
)

tagname = "generatoritem" if is_generatoritem else "clipitem"
clip_item_e = _element_with_item_metadata(tagname, clip_item)
if "frameBlend" not in clip_item_e.attrib:
clip_item_e.attrib["frameBlend"] = "FALSE"

if is_generator:
if is_generatoritem:
clip_item_e.append(_build_generator_effect(clip_item, br_map))
else:
clip_item_e.append(_build_file(clip_item.media_reference, br_map))
Expand Down Expand Up @@ -1680,7 +1741,7 @@ def _build_generator_effect(clip_item, br_map):
effect_element = _dict_to_xml_tree(fcp_xml_effect_info, "effect")

# Validate the metadata and make sure it contains the required elements
for required in ("effectid", "effecttype", "mediatype", "effectcategory"):
for required in ("effecttype", "mediatype", "effectcategory"):
if effect_element.find(required) is None:
return _build_empty_file(
generator_ref,
Expand All @@ -1690,6 +1751,9 @@ def _build_generator_effect(clip_item, br_map):

# Add the name
_append_new_sub_element(effect_element, "name", text=generator_ref.name)
_append_new_sub_element(
effect_element, "effectid", text=generator_ref.generator_kind
)

return effect_element

Expand Down
Loading