Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't Export Timeline with FCPX adapter (AttributeError: 'NoneType' object has no attribute 'get') #5

Open
philipph77 opened this issue Mar 4, 2024 · 0 comments

Comments

@philipph77
Copy link

I am trying to export a minimal timeline (only one audio track) with the fcpx_xml adapter, and open the timeline in Final Cut Pro X.
Exporting this timeline without adapters works without any problems. However, when using the fcpx_xml adater it raises an error.

Here is a minimal code example written in python and the stack trace:

import os
from pathlib import Path
from zipfile import ZipFile

from hachoir.parser import createParser
from hachoir.metadata import extractMetadata
import opentimelineio as otio


def write_zipfile(archive_name: str, timeline_file: Path, songfile: Path, clip_files: list[str], logdir: str) -> None:
    # writes the timeline_file together with the songfile and clip_files into one zip archive
    with ZipFile(archive_name, "w") as archive:
        for clip_file in clip_files: 
            archive.write(os.path.join(logdir, clip_file), os.path.join("media",clip_file))
        archive.write(os.path.join(timeline_file), timeline_file.name)
        archive.write(songfile, os.path.join("media",songfile.name))

def get_song_duration(songfile_path: Path):
    # returns the duration of the songfile in seconds
    song_parser = createParser(str(songfile_path.resolve()))
    song_metadata = extractMetadata(song_parser)
    return song_metadata.get("duration").total_seconds()

def main(songfile_path: Path):
    # create the timeline and track objects
    tl = otio.schema.Timeline(name="Storyline")
    tr_audio = otio.schema.Track(name="Audio", kind=otio.schema.Track.Kind.Audio)
    tl.tracks.append(tr_audio)

    # create the audio clip and append to the audio track
    song_duration = get_song_duration(songfile_path)
    cl_audio = otio.schema.Clip(
        name=str(songfile_path.stem),
        media_reference=otio.schema.ExternalReference(
            target_url="media/"+str(songfile_path.name),
            available_range=otio.opentime.TimeRange(
                start_time=otio.opentime.RationalTime(0, 1),
                duration=otio.opentime.RationalTime(song_duration, 1),
            )
        ),
        source_range=otio.opentime.TimeRange(
            start_time=otio.opentime.RationalTime(0, 1),
            duration=otio.opentime.RationalTime(song_duration, 1),
        )
    )
    tr_audio.append(cl_audio)

    # export timeline with fcpx_adapter
    fcp_xml_timeline_file = "timeline.fcpxml"
    otio.adapters.write_to_file(tl, fcp_xml_timeline_file, adapter_name="fcpx_xml")
    archive_name = "timeline_fcpx_xml.zip"
    write_zipfile(archive_name, Path(fcp_xml_timeline_file), songfile_path, [], "")
Traceback (most recent call last):
  File "/Users/FPJ21JY/Documents/Videographer/playground_fcpxml_adapter.py", line 60, in <module>
    main(songfile_path)
  File "/Users/FPJ21JY/Documents/Videographer/playground_fcpxml_adapter.py", line 50, in main
    otio.adapters.write_to_file(tl, fcp_xml_timeline_file, adapter_name="fcpx_xml")
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio/adapters/__init__.py", line 192, in write_to_file
    return adapter.write_to_file(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio/adapters/adapter.py", line 192, in write_to_file
    result = self.write_to_string(input_otio, **adapter_argument_map)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio/adapters/adapter.py", line 283, in write_to_string
    return self._execute_function(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio/plugins/python_plugin.py", line 153, in _execute_function
    return (getattr(self.module(), func_name)(**kwargs))
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio_contrib/adapters/fcpx_xml.py", line 1161, in write_to_string
    return FcpxOtio(input_otio).to_xml()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio_contrib/adapters/fcpx_xml.py", line 172, in to_xml
    top_sequence = self._stack_to_sequence(project.tracks)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio_contrib/adapters/fcpx_xml.py", line 248, in _stack_to_sequence
    self._track_for_spine(track, lane_id, spine, compound_clip)
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio_contrib/adapters/fcpx_xml.py", line 271, in _track_for_spine
    offset = self._offset_based_on_parent(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/FPJ21JY/anaconda3/envs/videographer/lib/python3.11/site-packages/opentimelineio_contrib/adapters/fcpx_xml.py", line 309, in _offset_based_on_parent
    parent.get("offset"),
    ^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant