From cc45e67543273a666a5eff49806f3d04e07cea56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFse=20Brosseau?= Date: Wed, 9 Oct 2024 17:40:12 -0400 Subject: [PATCH] Add otio string deserialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Éloïse Brosseau --- docs/rv-packages/rv-otio-reader.md | 1 + src/plugins/rv-packages/otio_reader/PACKAGE | 10 +++- .../otio_reader/annotation_schema.py | 59 +++++++++++++++++++ .../rv-packages/otio_reader/manifest.json | 6 ++ .../rv-packages/otio_reader/otio_reader.py | 25 +++++++- 5 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 src/plugins/rv-packages/otio_reader/annotation_schema.py diff --git a/docs/rv-packages/rv-otio-reader.md b/docs/rv-packages/rv-otio-reader.md index e755265e..c443dece 100644 --- a/docs/rv-packages/rv-otio-reader.md +++ b/docs/rv-packages/rv-otio-reader.md @@ -17,6 +17,7 @@ The RV package is installed in the usual Python package installation location: ` The package also uses a number of files located in `Plugins/SupportFiles/otio_reader` and detailed below. - `manifest.json`: Provides examples of schemas and hooks used in the import process. +- `annotation_schema.py` An example schema of an annotation. - `cdlExportHook.py`: An example of exporting an RVLinearize to a custom CDL effect in OTIO. - `cdlHook.py`: An example of importing a custom CDL effect in OTIO into an RVLinearize node. - `cdlSchema.py` An example schema of a CDL effect. diff --git a/src/plugins/rv-packages/otio_reader/PACKAGE b/src/plugins/rv-packages/otio_reader/PACKAGE index 0ce0d553..83186555 100644 --- a/src/plugins/rv-packages/otio_reader/PACKAGE +++ b/src/plugins/rv-packages/otio_reader/PACKAGE @@ -18,6 +18,8 @@ modes: files: - file: manifest.json location: SupportFiles/$PACKAGE + - file: annotation_schema.py + location: SupportFiles/$PACKAGE - file: cdlSchema.py location: SupportFiles/$PACKAGE - file: cdlHook.py @@ -83,6 +85,13 @@ description: |

+
  • +

    + annotation_schema.py + An example schema of an annotation. +

    +
  • +
  • cdlHook.py @@ -260,4 +269,3 @@ description: |

    For pre- and post- hooks, no return value is expected.

    - diff --git a/src/plugins/rv-packages/otio_reader/annotation_schema.py b/src/plugins/rv-packages/otio_reader/annotation_schema.py new file mode 100644 index 00000000..5044b363 --- /dev/null +++ b/src/plugins/rv-packages/otio_reader/annotation_schema.py @@ -0,0 +1,59 @@ +# ***************************************************************************** +# Copyright 2024 Autodesk, Inc. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# ***************************************************************************** + +""" +For our OTIO output to effectively interface with other programs +using the OpenTimelineIO Python API, our custom schema need to be +specified and registered with the API. + +As per OTIO documentation, a class such as this one must be created, +the schema must be registered with a PluginManifest, and the path to that +manifest must be added to $OTIO_PLUGIN_MANIFEST_PATH; then the schema +is ready to be used. + +Example: +myObject = otio.schemadef.Annotation.Annotation(name, visible, layers) +""" + +import opentimelineio as otio + + +@otio.core.register_type +class Annotation(otio.schema.Effect): + """A schema for annotations.""" + + _serializable_label = "Annotation.1" + _name = "Annotation" + + def __init__( + self, name: str = "", visible: bool = True, layers: list | None = None + ) -> None: + super().__init__(name=name, effect_name="Annotation.1") + self.visible = visible + self.layers = layers + + _visible = otio.core.serializable_field( + "visible", required_type=bool, doc=("Visible: expects either true or false") + ) + + _layers = otio.core.serializable_field( + "layers", required_type=list, doc=("Layers: expects a list of annotation types") + ) + + @property + def layers(self) -> list: + return self._layers + + @layers.setter + def layers(self, val: list): + self._layers = val + + def __str__(self) -> str: + return f"Annotation({self.name}, {self.effect_name}, {self.visible}, {self.layers})" + + def __repr__(self) -> str: + return f"otio.schema.Annotation(name={self.name!r}, effect_name={self.effect_name!r}, visible={self.visible!r}, layers={self.layers!r})" diff --git a/src/plugins/rv-packages/otio_reader/manifest.json b/src/plugins/rv-packages/otio_reader/manifest.json index 9c597843..ec2f121e 100644 --- a/src/plugins/rv-packages/otio_reader/manifest.json +++ b/src/plugins/rv-packages/otio_reader/manifest.json @@ -1,6 +1,12 @@ { "OTIO_SCHEMA" : "PluginManifest.1", "schemadefs" : [ + { + "OTIO_SCHEMA" : "SchemaDef.1", + "name" : "Annotation", + "execution_scope" : "in process", + "filepath" : "annotation_schema.py" + }, { "OTIO_SCHEMA" : "SchemaDef.1", "name" : "CDL", diff --git a/src/plugins/rv-packages/otio_reader/otio_reader.py b/src/plugins/rv-packages/otio_reader/otio_reader.py index 6e26a108..c389cf56 100644 --- a/src/plugins/rv-packages/otio_reader/otio_reader.py +++ b/src/plugins/rv-packages/otio_reader/otio_reader.py @@ -59,6 +59,24 @@ class NoNodeFromHook(otio.exceptions.OTIOError): pass +def read_otio_string(otio_string: str, host_prefix: str | None = None) -> object | None: + """ + Main entry point to expand a given otio string into the current RV session. + + Returns the top level node created that represents this otio + timeline. + """ + otio_obj = otio.adapters.read_from_string(otio_string) + timeline = otio_obj["otio"] + + if host_prefix is not None: + context = {"sg_url": host_prefix} + else: + context = None + + return create_rv_node_from_otio(timeline, context), timeline.global_start_time + + def read_otio_file(otio_file): """ Main entry point to expand a given otio (or file otio can read) @@ -276,7 +294,7 @@ def _create_track(in_seq, context=None): return new_seq -def _get_global_transform(tl): +def _get_global_transform(tl) -> dict: # since there's no global scale in otio, calculate the minimum box size # that can contain all clips def find_display_bounds(tl): @@ -491,9 +509,12 @@ def add_media(media_ref, active_key, cmd, *cmd_args): return source_group, active_source -def _get_media_path(target_url, context=None): +def _get_media_path(target_url: str, context: dict | None = None) -> str: context = context or {} + if "sg_url" in context: + return context.get("sg_url") + target_url + if not os.path.isabs(target_url): # if this is a relative file path, assume relative to the otio file otio_path = context.get("otio_file")