OpenTimelineIO error from exporting a Final Cut Pro file with the Kdenlive adapter

133 views Asked by At

Follow-up on Monkey-patching OpenTimelineIO adapter to import Final Cut Pro XML

I have several video projects from Final Cut Pro that I want to use in KdenLive. I found the OpenTimelineIO project and it would solve all my problems. I installed it, fixed the XML file following the suggestions from the thread above and am able to read with the FCP adapter.

I install opentimelineio from the PyPI, run otioconvert, and get an error related to the Kdenlive export adapter:

 $ otioconvert -i /path/to/file/ep4_fixed.fcpxml -o /path/to/file/ep4_fixed.kdenlive
Traceback (most recent call last):
  File "/usr/local/bin/otioconvert", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/opentimelineio/console/otioconvert.py", line 278, in main
    otio.adapters.write_to_file(
  File "/usr/local/lib/python3.11/site-packages/opentimelineio/adapters/__init__.py", line 192, in write_to_file
    return adapter.write_to_file(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/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 "/usr/local/lib/python3.11/site-packages/opentimelineio/adapters/adapter.py", line 283, in write_to_string
    return self._execute_function(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/opentimelineio/plugins/python_plugin.py", line 153, in _execute_function
    return (getattr(self.module(), func_name)(**kwargs))
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/opentimelineio_contrib/adapters/kdenlive.py", line 389, in write_to_string
    rate = input_otio.duration().rate
           ^^^^^^^^^^^^^^^^^^^
AttributeError: 'opentimelineio._otio.SerializableCollection' object has no attribute 'duration'

When I try to import it on Kdenlive, I get the same error:

  File "/usr/local/bin/otioconvert", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/opentimelineio/console/otioconvert.py", line 278, in main
    otio.adapters.write_to_file(
  File "/usr/local/lib/python3.11/site-packages/opentimelineio/adapters/__init__.py", line 192, in write_to_file
    return adapter.write_to_file(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/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 "/usr/local/lib/python3.11/site-packages/opentimelineio/adapters/adapter.py", line 283, in write_to_string
    return self._execute_function(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/opentimelineio/plugins/python_plugin.py", line 153, in _execute_function
    return (getattr(self.module(), func_name)(**kwargs))
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/opentimelineio_contrib/adapters/kdenlive.py", line 390, in write_to_string
    rate = input_otio.duration().rate
           ^^^^^^^^^^^^^^^^^^^
AttributeError: 'opentimelineio._otio.SerializableCollection' object has no attribute 'duration'

The error is distinct from that thread in that the offending collection is of this sort:

SerializableCollection(ep1-6, [otio.schema.Timeline(name='ep4', tracks=otio.schema.Stack(name='', children=[otio.schema.Track(name='-1', children=[otio.schema.Gap(name='',...

How can I tweak the XML file or the source code to fix this error and export for reading in Kdenlive?

The FCP XML file is here.

update

I tried this conversion under two methods.

First, I uninstalled opentimelineio (python3 -m pip uninstaled opentimelineio) and installed it from PyPI with python3 -m pip install opentimelineio. The kdenlive adapter is listed:

$ python3 -c "import opentimelineio as otio; print(otio.adapters.available_adapter_names())"
['otio_drp_adapter', 'fcp_xml', 'otio_json', 'otioz', 'otiod', 'cmx_3600', 'svg', 'fcpx_xml', 'hls_playlist', 'rv_session', 'maya_sequencer', 'ale', 'burnins', 'AAF', 'xges', 'kdenlive']

When I run otioconvert, as above, I get the same error as above (SerializableCollection has no attribute duration).

Second, I removed that installation and installed it from source, cloning the project from GitHub, from the latest commit of the main branch (version OpenTimelineIO-0.16.0.dev1, installed with python3 -m pip install .). Then, kdenlive is not listed as an adapter, and indeed kdenlive.py is missing from the directory contrib/adapters.

1

There are 1 answers

10
VonC On BEST ANSWER

Check first if this is similar to AcademySoftwareFoundation/OpenTimelineIO issue 1694, which mentions:

The default README is from the tip of the main branch, not the released OTIO. You can see the released one at https://github.com/AcademySoftwareFoundation/OpenTimelineIO/tree/v0.15.

So the actual readme is accurate since find_clips will be present in a yet to be released OTIO.

So you might need to tweak the OpenTimelineIO version to get the one with duration as an attribute.


If I understand correctly, you're suggesting that the version 0.16 will fix the issue.
I removed OpenTimelineIO installed with pip and installed from source from the tip of the main branch, and I get the error opentimelineio.exceptions.NoKnownAdapterForExtensionError: No adapter for suffix 'kdenlive' on file ....
So I can't yet say whether that version will work.

Make sure the Kdenlive adapter is included in the OpenTimelineIO plugins directory and that it is correctly implemented.
Since you are working with the source code, check the opentimelineio_contrib/adapters directory for a kdenlive.py file or similar. If you are working with the main branch, it is possible that new adapters or features are in development and not yet fully integrated or documented.

Also, OpenTimelineIO uses a plugin mechanism to discover and register adapters. You can check which adapters are currently recognized by your installation with adapters.available_adapter_names():

import opentimelineio as otio
print(otio.adapters.available_adapter_names())

If 'kdenlive' is not listed, the adapter is not correctly registered, which could be due to it not being included in the source code you have installed or due to an issue with the plugin discovery mechanism.
If the 'kdenlive' adapter is present in the source code but not registered, you might try manually invoking it. That would be a more complex and less recommended approach, given it bypasses the standard adapter mechanisms.


I tried again, from scratch, with the package from PyPI (it has kdenlive as an adapter but throws the same error) and from source (it's missing kdenlive as an adapter). I searched the GitHub repo for a branch that has both kdenlive as an adapter and duration as an attribute for SerializableCollection, but couldn't find it.

I'm not fiddling with the source code and trying to access duration, I'm only running otioconvert on the XML file from Final Cut Pro.

OK, so you mean the adapter is trying to call duration() on an object it assumes to be a Timeline but is actually a SerializableCollection. The adapter should either make sure it is operating on a Timeline object or implement logic to handle SerializableCollection appropriately.
As a temporary workaround until the adapter issue is resolved, you might consider writing a small script that ensures the input to the kdenlive adapter is a Timeline object. That would involve loading the .fcpxml file, extracting the Timeline from the SerializableCollection (if necessary), and then saving that as a .kdenlive file using the adapter programmatically.

import opentimelineio as otio

# Load the FCP XML file
input_otio = otio.adapters.read_from_file("/path/to/file/ep4_fixed.fcpxml")

# Extract the first Timeline from the SerializableCollection
timeline = next((item for item in input_otio if isinstance(item, otio.schema.Timeline)), None)

if timeline:
    # Save the timeline as a Kdenlive project
    otio.adapters.write_to_file(timeline, "/path/to/file/ep4_fixed.kdenlive", adapter_name="kdenlive")
else:
    print("No timeline found in the input file.")