AcademySoftwareFoundation / AcademySoftwareFoundation/OpenTimelineIO
ALE parser causes AttributeError: SerializableCollection / Clip object has no attribute 'tracks'
- Dominant language
- C++
- Stars
- 2k
- Forks
- 351
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 1
Description
## Bug Report
### Broken Functionality
Parts of the codebase assume the existence of a `tracks` attribute. However, ALE inputs don't have this. This is because the ALE adapter doesn't follow the "[Canonical Structure](https://opentimelineio.readthedocs.io/en/latest/tutorials/architecture.html#canonical-structure)" with a `Timeline` containing `tracks` which contain `clips`. Instead it uses a `SerializableCollection` containing `clips` directly.
## To Reproduce
`otiostat` outputs an error for every sample ALE file:
```sh
$ otiostat ./contrib/opentimelineio_contrib/adapters/tests/sample_data/*.ale
parsed: True
top level object: SerializableCollection.1
number of tracks: 0
There was a system error: 'opentimelineio._otio.SerializableCollection' object has no attribute 'tracks'
...
```
`otioconvert` also attempts to access a `tracks` attribute on the `SerializableCollection`:
```python
$ otioconvert -i ./contrib/opentimelineio_contrib/adapters/tests/sample_data/sample.ale -o out.edl
Traceback (most recent call last):
File "/Users/myuser/.virtualenvs/opentimeline-gh/bin/otioconvert", line 8, in
sys.exit(main())
File "/Users/myuser/dev/OpenTimelineIO/src/py-opentimelineio/opentimelineio/console/otioconvert.py", line 273, in main
otio.adapters.write_to_file(
File "/Users/myuser/dev/OpenTimelineIO/src/py-opentimelineio/opentimelineio/adapters/__init__.py", line 192, in write_to_file
return adapter.write_to_file(
File "/Users/myuser/dev/OpenTimelineIO/src/py-opentimelineio/opentimelineio/adapters/adapter.py", line 183, in write_to_file
result = self.write_to_string(input_otio, **adapter_argument_map)
File "/Users/myuser/dev/OpenTimelineIO/src/py-opentimelineio/opentimelineio/adapters/adapter.py", line 274, in write_to_string
return self._execute_function(
File "/Users/myuser/dev/OpenTimelineIO/src/py-opentimelineio/opentimelineio/plugins/python_plugin.py", line 142, in _execute_function
return (getattr(self.module(), func_name)(**kwargs))
File "/Users/myuser/dev/OpenTimelineIO/src/py-opentimelineio/opentimelineio/adapters/cmx_3600.py", line 800, in write_to_string
video_tracks = [t for t in input_otio.tracks
AttributeError: 'opentimelineio._otio.SerializableCollection' object has no attribute 'tracks'
```
`otioview` attempts to access a `tracks` attribute on the `Clip`, when you click an item in the sidebar:
```python
$ otioview ./contrib/opentimelineio_contrib/adapters/tests/sample_data/sampleUHD.ale
Traceback (most recent call last):
File "/Users/myuser/dev/OpenTimelineIO/src/opentimelineview/console.py", line 214, in _change_track
self.timeline_widget.set_timeline(selection[0].timeline)
File "/Users/myuser/dev/OpenTimelineIO/src/opentimelineview/timeline_widget.py", line 785, in set_timeline
self.add_stack(timeline.tracks)
AttributeError: 'opentimelineio._otio.Clip' object has no attribute 'tracks'
```
## Expected Behavior
Adapters should not create data structures that other parts of the library don't know how to handle.
I'm not familiar enough with this library or why the ale adapter was created this way, to know the correct solution. Here are the two options I see:
### 1. Handle differences and catch errors
The tracebacks above should be inspected and we attempt to proceed as best as possible given the different schemas used. Simple to catch the error in `otiostat`, but for `otioview` and `otioconvert` we may have to add special case code or error with a friendly message explaining this command isn't available with this datatype.
### 2. Convert to using Canonical Structure
Amend the ALE adapter to create objects in the canonical format. Perhaps behind a flag, for backwards compatibility?
I've amended a few lines, just to test the feasibility of this. See https://github.com/AcademySoftwareFoundation/OpenTimelineIO/compare/main...tomviner:OpenTimelineIO:ale-adapter-attrib-errors It's totally incomplete, but it does enable the commands to function:
#### `otiostat`
**Before**
```sh
$ otiostat ./contrib/opentimelineio_contrib/adapters/tests/sample_data/sample.ale
parsed: True
top level object: SerializableCollection.1
number of tracks: 0
There was a system error: 'opentimelineio._otio.SerializableCollection' object has no attribute 'tracks'
deepest nesting: 1
number of clips: 4
total duration: n/a
total duration in timecode: n/a
top level rate: n/a
clips with cdl data: 0
Tracks with non standard types: 0
```
**With patch**
```sh
$ otiostat ./contrib/opentimelineio_contrib/adapters/tests/sample_data/sample.ale
parsed: True
top level object: Timeline.1
number of tracks: 1
Tracks are the same length: True
deepest nesting: 4
number of clips: 4
total duration: RationalTime(402, 24)
total duration in timecode: 00:00:16:18
top level rate: 24.0
clips with cdl data: 0
Tracks with non standard types: 0
```
**Diff**
Note the error message, which is actually sent to stderr, is fixed. And note the duration is now filled in.
```diff
-There was a system error: 'opentimelineio._otio.SerializableCollection' object has no attribute 'tracks'
parsed: True
-top level object: SerializableCollection.1
-number of tracks: 0
-deepest nesting: 1
+top level object: Timeline.1
+number of tracks: 1
+Tracks are the same length: True
+deepest nesting: 4
number of clips: 4
-total duration: n/a
-total duration in timecode: n/a
-top level rate: n/a
+total duration: RationalTime(402, 24)
+total duration in timecode: 00:00:16:18
+top level rate: 24.0
clips with cdl data: 0
Tracks with non standard types: 0
```
#### `otioconvert`
This now works without an exception. Although round-tripping back to ALE loses a lot of data, as my patch is incomplete.
```python
$ otioconvert -i ./contrib/opentimelineio_contrib/adapters/tests/sample_data/sample.ale -o out.edl
```
#### `otioview`
```sh
$ otioview ./contrib/opentimelineio_contrib/adapters/tests/sample_data/sample.ale
```
There's now no sidebar, just a timeline:
---
Thoughts?
Contributor guide
Research direction
Start with the ALE adapter and the reported entry points in otiostat, cmx_3600.py, and opentimelineview/console.py and timeline_widget.py. Reproduce the failures using the sample ALE files and trace how SerializableCollection and Clip reach code expecting tracks. Done means the affected commands no longer raise AttributeError for ALE input, with behavior covered by relevant tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100