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

Integration fails on publishing with recent OpenTimelineIO versions (0.16.0+) #2

Closed
2 tasks done
BigRoy opened this issue Jun 27, 2024 · 3 comments · Fixed by #15
Closed
2 tasks done

Integration fails on publishing with recent OpenTimelineIO versions (0.16.0+) #2

BigRoy opened this issue Jun 27, 2024 · 3 comments · Fixed by #15
Assignees
Labels
type: bug Something isn't working

Comments

@BigRoy
Copy link
Contributor

BigRoy commented Jun 27, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior:

As can be seen in this community forum post the Resolve publishing logic fails on Precollect Instances:

image

Expected Behavior:

Recent OpenTimelineIO versions should work fine.

But, for now we could also update documentation that it requires OpenTimelineIO 0.15.0, so along the lines of:

pip install OpenTimelineIO==0.15.0

Version

1.0.0

What platform you are running on?

Windows

Steps To Reproduce:

  1. Use e.g. Python 3.9 (or other Py versions like 3.10) with recent OpenTimelineIO, e.g. 0.16.0 or 0.17.0
  2. Run Resolve
  3. Create publishable clip
  4. Publish

Are there any labels you wish to add?

  • I have added the relevant labels to the bug report.

Relevant log output:

Traceback (most recent call last):
  File "C:\Users\User\AppData\Local\Ynput\AYON\dependency_packages\ayon_2403061937_windows.zip\dependencies\pyblish\plugin.py", line 527, in __explicit_process
    runner(*args)
  File "E:\dev\ayon-core\server_addon\resolve\client\ayon_resolve\plugins\publish\precollect_instances.py", line 114, in process
  File "E:\dev\ayon-core\server_addon\resolve\client\ayon_resolve\api\lib.py", line 912, in get_otio_clip_instance_data
    for otio_clip in otio_timeline.each_clip():
AttributeError: 'opentimelineio._otio.Timeline' object has no attribute 'each_clip'

Additional context:

No response

@BigRoy BigRoy added the type: bug Something isn't working label Jun 27, 2024
@BigRoy
Copy link
Contributor Author

BigRoy commented Jun 27, 2024

This is a part of the help(timeline) using OpenTimelineIO==0.16.0:

class Timeline(SerializableObjectWithMetadata)
 |  Method resolution order:
 |      Timeline
 |      SerializableObjectWithMetadata
 |      SerializableObject
 |      pybind11_builtins.pybind11_object
 |      builtins.object
 |  
 |  Methods defined here:
 |  
 |  __init__(...)
 |      __init__(self: opentimelineio._otio.Timeline, name: str = '', tracks: Optional[List[opentimelineio._otio.Composable]] = None, global_start_time: Optional[opentimelineio._opentime.RationalTime] = None, metadata: object = None) -> None
 |  
 |  __repr__(self)
 |  
 |  __str__(self)
 |  
 |  audio_tracks(...)
 |      audio_tracks(self: opentimelineio._otio.Timeline) -> List[opentimelineio._otio.Track]
 |  
 |  duration(...)
 |      duration(self: opentimelineio._otio.Timeline) -> opentimelineio._opentime.RationalTime
 |  
 |  find_children(...)
 |      find_children(self: opentimelineio._otio.Timeline, descended_from_type: object = None, search_range: Optional[opentimelineio._opentime.TimeRange] = None, shallow_search: bool = False) -> List[opentimelineio._otio.SerializableObject]
 |  
 |  find_clips(...)
 |      find_clips(self: opentimelineio._otio.Timeline, search_range: Optional[opentimelineio._opentime.TimeRange] = None, shallow_search: bool = False) -> List[opentimelineio._otio.SerializableObject]
 |  
 |  range_of_child(...)
 |      range_of_child(self: opentimelineio._otio.Timeline, arg0: opentimelineio._otio.Composable) -> opentimelineio._opentime.TimeRange
 |  
 |  video_tracks(...)
 |      video_tracks(self: opentimelineio._otio.Timeline) -> List[opentimelineio._otio.Track]

I believe we might need to use timeline.find_clips() which does seem to work.


Yet that does not work in OpenTimelineIO==0.15.0.
Error:

Traceback (most recent call last):
  File "C:\Users\User\AppData\Local\Ynput\AYON\dependency_packages\ayon_2403061937_windows.zip\dependencies\pyblish\plugin.py", line 527, in __explicit_process
    runner(*args)
  File "E:\dev\ayon-core\server_addon\resolve\client\ayon_resolve\plugins\publish\precollect_instances.py", line 119, in process
  File "E:\dev\ayon-core\server_addon\resolve\client\ayon_resolve\api\lib.py", line 913, in get_otio_clip_instance_data
    for otio_clip in otio_timeline.find_clips():
AttributeError: 'opentimelineio._otio.Timeline' object has no attribute 'find_clips'

And some info about the timeline object:

class Timeline(SerializableObjectWithMetadata)
 |  Method resolution order:
 |      Timeline
 |      SerializableObjectWithMetadata
 |      SerializableObject
 |      pybind11_builtins.pybind11_object
 |      builtins.object
 |  
 |  Methods defined here:
 |  
 |  __init__(...)
 |      __init__(self: opentimelineio._otio.Timeline, name: str = '', tracks: object = None, global_start_time: Optional[opentimelineio._opentime.RationalTime] = None, metadata: object = None) -> None
 |  
 |  __repr__(self)
 |  
 |  __str__(self)
 |  
 |  audio_tracks(...)
 |      audio_tracks(self: opentimelineio._otio.Timeline) -> List[opentimelineio._otio.Track]
 |  
 |  children_if(...)
 |      children_if(self: opentimelineio._otio.Timeline, descended_from_type: object = None, search_range: Optional[opentimelineio._opentime.TimeRange] = None, shallow_search: bool = False) -> List[opentimelineio._otio.SerializableObject]
 |  
 |  clip_if(...)
 |      clip_if(self: opentimelineio._otio.Timeline, search_range: Optional[opentimelineio._opentime.TimeRange] = None, shallow_search: bool = False) -> List[opentimelineio._otio.SerializableObject]
 |  
 |  duration(...)
 |      duration(self: opentimelineio._otio.Timeline) -> opentimelineio._opentime.RationalTime
 |  
 |  each_child(self, search_range=None, descended_from_type=<class 'opentimelineio._otio.Composable'>, shallow_search=False)
 |      Generator that returns each child contained in the timeline
 |      in the order in which it is found.
 |      
 |      .. deprecated:: 0.14.0
 |          Use :meth:`children_if` instead.
 |      
 |      :param TimeRange search_range: if specified, only children whose range overlaps
 |                                     with the search range will be yielded.
 |      :param type descended_from_type: if specified, only children who are a descendent
 |                                       of the descended_from_type will be yielded.
 |      :param bool shallow_search: if True, will only search children of self and not
 |                                  recurse into children of children.
 |  
 |  each_clip(self, search_range=None, shallow_search=False)
 |      Generator that returns each clip contained in the timeline
 |      in the order in which it is found.
 |      
 |      .. deprecated:: 0.14.0
 |          Use :meth:`clip_if` instead.
 |      
 |      :param TimeRange search_range: if specified, only children whose range overlaps
 |                                     with the search range will be yielded.
 |      :param bool shallow_search: if True, will only search children of self and not
 |                                  recurse into children of children.
 |  
 |  range_of_child(...)
 |      range_of_child(self: opentimelineio._otio.Timeline, arg0: opentimelineio._otio.Composable) -> opentimelineio._opentime.TimeRange
 |  
 |  video_tracks(...)
 |      video_tracks(self: opentimelineio._otio.Timeline) -> List[opentimelineio._otio.Track]

That help shows that each_clip was indeed deprecated there and recommends using clip_if which does work in 0.15.0 but actually does not work in 0.16.0

image

So much fun. We might just need to use a wrapper function to pick the right method based on version or available methods. :)

Like for example:

def each_clip(timeline):
    if hasattr(timeline, "find_clips"):
        # OpenTimelineIO 0.16.0+
        return timeline.find_clips()
    elif hasattr(timeline, "clip_if"):
        # OpenTimelineIO 0.15.0+
        return timeline.clip_if()
    else:
        # OpenTimelineIO - older?
        return timeline.each_clip()

@BigRoy
Copy link
Contributor Author

BigRoy commented Jul 2, 2024

@antirotor what would you like to do here? :)

@BigRoy
Copy link
Contributor Author

BigRoy commented Jul 31, 2024

This may be more urgent now - we've gotten a report that Resolve's python interpreter just 'crashes' and logs nothing more as soon as it imports OpenTimelineIO version 0.17.0 (tested on Windows).

  1. We will want to at least update the docs to ensure 0.15.0 gets installed by most people? (or do it in another way?)
  2. Find some way to safeguard against the crash and report invalid versions.

I posted elsewhere the todo:

  1. Definitely something we should track as issue so that the logs are clearer for the script

    • Force a print statement before the imports to ensure we know at least the script run started (for future debugging)
    • Somehow safeguard the import of OpenTimelineIO
    • Preferably report if a "known invalid" OpenTimelineIO version is installed (if we can do that without importing it, because importing it.. crashes it) | of course to get there we may want to first reproduce the crash on our end to with 0.17.0 to confirm)
  2. Improve the README and installation instruction for Resolve to state to install OpenTimelineIO 0.15.0 to avoid hard to debug issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants