From 31175c50970c2fd31b8cef634948fc5a54025df9 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Fri, 25 Sep 2020 02:13:11 +0530 Subject: [PATCH 01/15] Initialize subtitles schema implementation --- src/opentimelineio/CMakeLists.txt | 6 ++ src/opentimelineio/subtitles.cpp | 97 +++++++++++++++++++++ src/opentimelineio/subtitles.h | 111 ++++++++++++++++++++++++ src/opentimelineio/timedText.cpp | 31 +++++++ src/opentimelineio/timedText.h | 61 +++++++++++++ src/opentimelineio/timedTextStyle.cpp | 52 +++++++++++ src/opentimelineio/timedTextStyle.h | 119 ++++++++++++++++++++++++++ 7 files changed, 477 insertions(+) create mode 100644 src/opentimelineio/subtitles.cpp create mode 100644 src/opentimelineio/subtitles.h create mode 100644 src/opentimelineio/timedText.cpp create mode 100644 src/opentimelineio/timedText.h create mode 100644 src/opentimelineio/timedTextStyle.cpp create mode 100644 src/opentimelineio/timedTextStyle.h diff --git a/src/opentimelineio/CMakeLists.txt b/src/opentimelineio/CMakeLists.txt index 1c9dc14495..8c558baeeb 100644 --- a/src/opentimelineio/CMakeLists.txt +++ b/src/opentimelineio/CMakeLists.txt @@ -30,6 +30,9 @@ set(OPENTIMELINEIO_HEADER_FILES stack.h stackAlgorithm.h stringUtils.h + subtitles.h + timedText.h + timedTextStyle.h timeEffect.h timeline.h track.h @@ -65,6 +68,9 @@ add_library(opentimelineio ${OTIO_SHARED_OR_STATIC_LIB} stack.cpp stackAlgorithm.cpp stringUtils.cpp + subtitles.cpp + timedText.cpp + timedTextStyle.cpp timeEffect.cpp timeline.cpp track.cpp diff --git a/src/opentimelineio/subtitles.cpp b/src/opentimelineio/subtitles.cpp new file mode 100644 index 0000000000..b0d55ef436 --- /dev/null +++ b/src/opentimelineio/subtitles.cpp @@ -0,0 +1,97 @@ +#include "opentimelineio/subtitles.h" + +namespace opentimelineio { + namespace OPENTIMELINEIO_VERSION { + + Subtitles::Subtitles(float extent_x, + float extent_y, + float padding_x, + float padding_y, + std::string background_color, + float background_opacity, + DisplayAlignment display_alignment, + std::vector timed_texts) + : Parent(), + _extent_x(extent_x), + _extent_y(extent_y), + _padding_x(padding_x), + _padding_y(padding_y), + _background_color(background_color), + _background_opacity(background_opacity), + _display_alignment(display_alignment), + _timed_texts(timed_texts.begin(), timed_texts.end()) { + } + + Subtitles::~Subtitles() noexcept {} + + TimeRange Subtitles::available_range(ErrorStatus *) const { + TimeRange timeRange; + for (auto timed_text : _timed_texts) { + timeRange = timeRange.extended_by(timed_text.value->marked_range()); + } + return timeRange; + } + + bool Subtitles::read_from(Reader &reader) { + auto result = reader.read("extent_x", &_extent_x) && + reader.read("extent_y", &_extent_y) && + reader.read("padding_x", &_padding_x) && + reader.read("padding_y", &_padding_y) && + reader.read("background_color", &_background_color) && + reader.read("background_opacity", &_background_opacity) && + reader.read("timed_texts", &_timed_texts); + std::string display_alignment_value; + result = result && reader.read("display_alignment", &display_alignment_value); + if (!result) { + return result; + } + + if (display_alignment_value == "before") { + _display_alignment = DisplayAlignment::before; + } else if (display_alignment_value == "after") { + _display_alignment = DisplayAlignment::after; + } else if (display_alignment_value == "center") { + _display_alignment = DisplayAlignment::justify; + } else if (display_alignment_value == "justify") { + _display_alignment = DisplayAlignment::justify; + } else { + // Unrecognized value + ErrorStatus error_status = ErrorStatus(ErrorStatus::JSON_PARSE_ERROR, + "Unknown display_alignment: " + display_alignment_value); + reader.error(error_status); + return false; + } + + return Parent::read_from(reader); + } + + void Subtitles::write_to(Writer &writer) const { + Parent::write_to(writer); + writer.write("extent_x", _extent_x); + writer.write("extent_y", _extent_y); + writer.write("padding_x", _padding_x); + writer.write("padding_y", _padding_y); + writer.write("background_color", _background_color); + writer.write("background_opacity", _background_opacity); + writer.write("timed_texts", _timed_texts); + + std::string display_alignment_value; + switch (_display_alignment) { + case DisplayAlignment::before: + display_alignment_value = "before"; + break; + case DisplayAlignment::after: + display_alignment_value = "after"; + break; + case DisplayAlignment::center: + display_alignment_value = "center"; + break; + case DisplayAlignment::justify: + display_alignment_value = "justify"; + break; + } + writer.write("display_alignment", display_alignment_value); + } + + } +} diff --git a/src/opentimelineio/subtitles.h b/src/opentimelineio/subtitles.h new file mode 100644 index 0000000000..2f8d2b1059 --- /dev/null +++ b/src/opentimelineio/subtitles.h @@ -0,0 +1,111 @@ +#pragma once + +#include "opentimelineio/version.h" +#include "opentimelineio/item.h" +#include "opentimelineio/timedText.h" + +namespace opentimelineio { + namespace OPENTIMELINEIO_VERSION { + class Subtitles : public Item { + public: + enum DisplayAlignment { + before = 0, + after = 1, + center = 2, + justify = 3 + }; + + struct Schema { + static auto constexpr name = "Subtitles"; + static int constexpr version = 1; + }; + + using Parent = Item; + + Subtitles(float extent_x = 0.f, + float extent_y = 0.f, + float padding_x = 0.f, + float padding_y = 0.f, + std::string background_color = std::string(), + float background_opacity = 0.f, + DisplayAlignment display_alignment = DisplayAlignment::after, + std::vector timed_texts = std::vector()); + + virtual TimeRange available_range(ErrorStatus *error_status) const; + + float const extent_x() const { + return _extent_x; + } + + void set_extent_x(float const extent_x) { + _extent_x = extent_x; + } + + float const extent_y() const { + return _extent_y; + } + + void set_extent_y(float const extent_y) { + _extent_y = extent_y; + } + + float const padding_x() const { + return _padding_x; + } + + void set_padding_x(float const padding_x) { + _padding_x = padding_x; + } + + float const padding_y() const { + return _padding_y; + } + + void set_padding_y(float const padding_y) { + _padding_y = padding_y; + } + + std::string const &background_color() const { + return _background_color; + } + + void set_background_color(std::string const &background_color) { + _background_color = background_color; + } + + float const background_opacity() const { + return _background_opacity; + } + + void set_background_opacity(float const background_opacity) { + _background_opacity = background_opacity; + } + + DisplayAlignment const display_alignment() const { + return _display_alignment; + } + + void set_display_alignment(DisplayAlignment const display_alignment) { + _display_alignment = display_alignment; + } + + protected: + ~Subtitles(); + + virtual bool read_from(Reader &); + + virtual void write_to(Writer &) const; + + private: + float _extent_x; + float _extent_y; + float _padding_x; + float _padding_y; + std::string _background_color; + float _background_opacity; + DisplayAlignment _display_alignment; + std::vector> _timed_texts; + }; + + } +} diff --git a/src/opentimelineio/timedText.cpp b/src/opentimelineio/timedText.cpp new file mode 100644 index 0000000000..211be3f36f --- /dev/null +++ b/src/opentimelineio/timedText.cpp @@ -0,0 +1,31 @@ +#include "opentimelineio/timedText.h" + +namespace opentimelineio { + namespace OPENTIMELINEIO_VERSION { + + TimedText::TimedText(std::string const &text, + RationalTime const &in_time, + RationalTime const &out_time, + TimedTextStyle *style) + : Parent(), + _text(text), + _style(style) { + set_marked_range(TimeRange::range_from_start_end_time(in_time, out_time)); + } + + TimedText::~TimedText() {} + + bool TimedText::read_from(Reader &reader) { + return reader.read("text", &_text) && + reader.read("style", &_style) && + Parent::read_from(reader); + } + + void TimedText::write_to(Writer &writer) const { + Parent::write_to(writer); + writer.write("text", _text); + writer.write("style", _style); + } + + } +} diff --git a/src/opentimelineio/timedText.h b/src/opentimelineio/timedText.h new file mode 100644 index 0000000000..6db8728d95 --- /dev/null +++ b/src/opentimelineio/timedText.h @@ -0,0 +1,61 @@ +#pragma once + +#include "opentimelineio/version.h" +#include "opentimelineio/marker.h" +#include "opentimelineio/timedTextStyle.h" + +namespace opentimelineio { + namespace OPENTIMELINEIO_VERSION { + + class TimedText : public Marker { + public: + struct Schema { + static auto constexpr name = "TimedText"; + static int constexpr version = 1; + }; + + using Parent = Marker; + + TimedText(std::string const &text = std::string(), + RationalTime const &in_time = RationalTime(), + RationalTime const &out_time = RationalTime(), + TimedTextStyle *style = new TimedTextStyle()); + + std::string const &text() const { + return _text; + } + + void set_text(std::string const &text) { + _text = text; + } + + Retainer const style() const { + return _style; + } + + void set_style(TimedTextStyle *style) { + _style = style; + } + + RationalTime const in_time() const { + return marked_range().start_time(); + } + + RationalTime out_time() const { + return marked_range().end_time_inclusive(); + } + + protected: + ~TimedText(); + + virtual bool read_from(Reader &); + + virtual void write_to(Writer &) const; + + private: + std::string _text; + Retainer _style; + }; + + } +} diff --git a/src/opentimelineio/timedTextStyle.cpp b/src/opentimelineio/timedTextStyle.cpp new file mode 100644 index 0000000000..8af34f9592 --- /dev/null +++ b/src/opentimelineio/timedTextStyle.cpp @@ -0,0 +1,52 @@ +#include "opentimelineio/timedTextStyle.h" + +namespace opentimelineio { + namespace OPENTIMELINEIO_VERSION { + + TimedTextStyle::TimedTextStyle(const std::string &style_id, + std::string text_alignment, + std::string text_color, + float text_size, + bool text_bold, + bool text_italics, + bool text_underline, + std::string font_family) + : Parent(), + _style_id(style_id), + _text_alignment(text_alignment), + _text_color(text_color), + _text_size(text_size), + _text_bold(text_bold), + _text_italics(text_italics), + _text_underline(text_underline), + _font_family(font_family) { + } + + TimedTextStyle::~TimedTextStyle() {} + + bool TimedTextStyle::read_from(Reader &reader) { + return reader.read("style_id", &_style_id) && + reader.read("text_alignment", &_text_alignment) && + reader.read("text_color", &_text_color) && + reader.read("text_size", &_text_size) && + reader.read("text_bold", &_text_bold) && + reader.read("text_italics", &_text_italics) && + reader.read("text_underline", &_text_underline) && + reader.read("font_family", &_font_family) && + Parent::read_from(reader); + } + + void TimedTextStyle::write_to(Writer &writer) const { + Parent::write_to(writer); + writer.write("style_id", _style_id); + writer.write("text_alignment", _text_alignment); + writer.write("text_color", _text_color); + writer.write("text_size", _text_size); + writer.write("text_bold", _text_bold); + writer.write("text_italics", _text_italics); + writer.write("text_underline", _text_underline); + writer.write("font_family", _font_family); + } + + } +} diff --git a/src/opentimelineio/timedTextStyle.h b/src/opentimelineio/timedTextStyle.h new file mode 100644 index 0000000000..5191d62a8b --- /dev/null +++ b/src/opentimelineio/timedTextStyle.h @@ -0,0 +1,119 @@ +#pragma once + +#include "opentimelineio/version.h" +#include "opentimelineio/serializableObjectWithMetadata.h" +#include "opentimelineio/marker.h" + +namespace opentimelineio { + namespace OPENTIMELINEIO_VERSION { + + class TimedTextStyle : public SerializableObject { + public: + struct TextAlignment { + static auto constexpr left = "LEFT"; + static auto constexpr top = "TOP"; + static auto constexpr right = "RIGHT"; + static auto constexpr bottom = "BOTTOM"; + static auto constexpr center = "CENTER"; + }; + + struct Schema { + static auto constexpr name = "TimedTextStyle"; + static int constexpr version = 1; + }; + + using Parent = SerializableObject; + + TimedTextStyle(std::string const &style_id = std::string(), + std::string text_alignment = TextAlignment::bottom, + std::string text_color = Marker::Color::black, + float text_size = 10, + bool text_bold = false, + bool text_italics = false, + bool text_underline = false, + std::string font_family = std::string()); + + std::string const &style_id() const { + return _style_id; + } + + void set_style_id(std::string const &style_id) { + _style_id = style_id; + } + + std::string const &text_alignment() const { + return _text_alignment; + } + + void set_text_alignment(std::string const &text_alignment) { + _text_alignment = text_alignment; + } + + std::string const &text_color() const { + return _text_color; + } + + void set_text_color(std::string const &text_color) { + _text_color = text_color; + } + + float const text_size() const { + return _text_size; + } + + void set_text_size(float const text_size) { + _text_size = text_size; + } + + bool const is_text_bold() const { + return _text_bold; + } + + void set_text_bold(bool const text_bold) { + _text_bold = text_bold; + } + + bool const is_text_italicized() const { + return _text_italics; + } + + void set_text_italicized(bool const text_italics) { + _text_italics = text_italics; + } + + bool const is_text_underlined() const { + return _text_underline; + } + + void set_text_underlined(bool const text_underline) { + _text_underline = text_underline; + } + + std::string const &font_family() const { + return _font_family; + } + + void set_font_family(std::string const &font_family) { + _font_family = font_family; + } + + protected: + ~TimedTextStyle(); + + virtual bool read_from(Reader &); + + virtual void write_to(Writer &) const; + + private: + std::string _text_color; + std::string _style_id; + std::string _text_alignment; + float _text_size; + bool _text_bold; + bool _text_italics; + bool _text_underline; + std::string _font_family; + }; + + } +} From ad63606b1a42bef78c58b4d1d08efa8b602052c0 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Fri, 2 Oct 2020 23:35:02 +0530 Subject: [PATCH 02/15] Add python bindings for TimedTextStyle --- .../otio_serializableObjects.cpp | 55 +++++++++++++++++++ .../opentimelineio/schema/__init__.py | 2 + .../opentimelineio/schema/timed_text_style.py | 44 +++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/py-opentimelineio/opentimelineio/schema/timed_text_style.py diff --git a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp index 612b9107bf..0a352c4379 100644 --- a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp +++ b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp @@ -22,6 +22,7 @@ #include "opentimelineio/timeline.h" #include "opentimelineio/track.h" #include "opentimelineio/transition.h" +#include "opentimelineio/timedTextStyle.h" #include "opentimelineio/serializableCollection.h" #include "opentimelineio/stack.h" #include "opentimelineio/unknownSchema.h" @@ -195,6 +196,60 @@ static void define_bases1(py::module m) { .def_property("name", [](SOWithMetadata* so) { return plain_string(so->name()); }, &SOWithMetadata::set_name); + + auto timed_text_style_class = py::class_>(m, "TimedTextStyle", py::dynamic_attr()) + .def(py::init([](std::string style_id, + std::string text_alignment, + std::string text_color, + float text_size, + bool text_bold, + bool text_italics, + bool text_underline, + std::string font_family) { + return new TimedTextStyle(style_id, text_alignment, text_color, text_size, text_bold, text_italics, text_underline); + }), + "style_id"_a = std::string(), + "text_alignment"_a = TimedTextStyle::TextAlignment::bottom, + "text_color"_a = Marker::Color::black, + "text_size"_a = 10.0f, + "text_bold"_a = false, + "text_italics"_a = false, + "text_underline"_a = false, + "font_family"_a = std::string()); + + py::class_(timed_text_style_class, "TextAlignment") + .def_property_readonly_static("LEFT", [](py::object /* self */) { return TimedTextStyle::TextAlignment::left; }) + .def_property_readonly_static("TOP", [](py::object /* self */) { return TimedTextStyle::TextAlignment::top; }) + .def_property_readonly_static("RIGHT", [](py::object /* self */) { return TimedTextStyle::TextAlignment::right; }) + .def_property_readonly_static("BOTTOM", [](py::object /* self */) { return TimedTextStyle::TextAlignment::bottom; }) + .def_property_readonly_static("CENTER", [](py::object /* self */) { return TimedTextStyle::TextAlignment::center; }); + + timed_text_style_class + .def_property("style_id", [](TimedTextStyle* tts) { + return plain_string(tts->style_id()); + }, &TimedTextStyle::set_style_id) + .def_property("text_alignment", [](TimedTextStyle* tts) { + return plain_string(tts->text_alignment()); + }, &TimedTextStyle::set_text_alignment) + .def_property("text_color", [](TimedTextStyle* tts) { + return plain_string(tts->text_color()); + }, &TimedTextStyle::set_text_color) + .def_property("text_size", [](TimedTextStyle* tts) { + return tts->text_size(); + }, &TimedTextStyle::set_text_size) + .def_property("text_bold", [](TimedTextStyle* tts) { + return tts->is_text_bold(); + }, &TimedTextStyle::set_text_bold) + .def_property("text_italics", [](TimedTextStyle* tts) { + return tts->is_text_italicized(); + }, &TimedTextStyle::set_text_italicized) + .def_property("text_underline", [](TimedTextStyle* tts) { + return tts->is_text_underlined(); + }, &TimedTextStyle::set_text_underlined) + .def_property("font_family", [](TimedTextStyle* tts) { + return plain_string(tts->font_family()); + }, &TimedTextStyle::set_font_family); } static void define_bases2(py::module m) { diff --git a/src/py-opentimelineio/opentimelineio/schema/__init__.py b/src/py-opentimelineio/opentimelineio/schema/__init__.py index f390bbbd4d..aa143c58c9 100644 --- a/src/py-opentimelineio/opentimelineio/schema/__init__.py +++ b/src/py-opentimelineio/opentimelineio/schema/__init__.py @@ -40,6 +40,7 @@ MissingReference, SerializableCollection, Stack, + TimedTextStyle, Timeline, Track, Transition @@ -63,6 +64,7 @@ marker, serializable_collection, stack, + timed_text_style, timeline, track, transition, diff --git a/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py b/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py new file mode 100644 index 0000000000..cea92fc9f4 --- /dev/null +++ b/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py @@ -0,0 +1,44 @@ +from .. core._core_utils import add_method +from .. import _otio + + +@add_method(_otio.TimedTextStyle) +def __str__(self): + return ( + 'TimedTextStyle(' + '"{}", "{}", "{}", {}, {}, {}, {}, {})' .format( + self.style_id, + self.text_alignment, + self.text_color, + self.text_size, + self.text_bold, + self.text_italics, + self.text_underline, + self.font_family + ) + ) + + +@add_method(_otio.TimedTextStyle) +def __repr__(self): + return ( + 'TimedTextStyle(' + 'style_id={}, ' + 'text_alignment={}, ' + 'text_color={}, ' + 'text_size={}, ' + 'text_bold={}, ' + 'text_italics={}, ' + 'text_underline={}, ' + 'font_family={}' + ')' .format( + repr(self.style_id), + repr(self.text_alignment), + repr(self.text_color), + repr(self.text_size), + repr(self.text_bold), + repr(self.text_italics), + repr(self.text_underline), + repr(self.font_family), + ) + ) \ No newline at end of file From b6abab21952015ee8ad102b22583a0ff49a26941 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Sat, 3 Oct 2020 01:28:32 +0530 Subject: [PATCH 03/15] Rebase with main --- src/opentimelineio/timedText.h | 4 +-- .../otio_serializableObjects.cpp | 31 +++++++++++++++++- .../opentimelineio/schema/__init__.py | 2 ++ .../opentimelineio/schema/timed_text.py | 32 +++++++++++++++++++ 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/py-opentimelineio/opentimelineio/schema/timed_text.py diff --git a/src/opentimelineio/timedText.h b/src/opentimelineio/timedText.h index 6db8728d95..968d865c54 100644 --- a/src/opentimelineio/timedText.h +++ b/src/opentimelineio/timedText.h @@ -29,8 +29,8 @@ namespace opentimelineio { _text = text; } - Retainer const style() const { - return _style; + TimedTextStyle* style() const { + return _style.value; } void set_style(TimedTextStyle *style) { diff --git a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp index 0a352c4379..16c2181cf0 100644 --- a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp +++ b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp @@ -22,6 +22,7 @@ #include "opentimelineio/timeline.h" #include "opentimelineio/track.h" #include "opentimelineio/transition.h" +#include "opentimelineio/timedText.h" #include "opentimelineio/timedTextStyle.h" #include "opentimelineio/serializableCollection.h" #include "opentimelineio/stack.h" @@ -207,7 +208,14 @@ static void define_bases1(py::module m) { bool text_italics, bool text_underline, std::string font_family) { - return new TimedTextStyle(style_id, text_alignment, text_color, text_size, text_bold, text_italics, text_underline); + return new TimedTextStyle(style_id, + text_alignment, + text_color, + text_size, + text_bold, + text_italics, + text_underline, + font_family); }), "style_id"_a = std::string(), "text_alignment"_a = TimedTextStyle::TextAlignment::bottom, @@ -348,6 +356,27 @@ static void define_bases2(py::module m) { .def("children_if", [](SerializableCollection* t, py::object descended_from_type, optional const& search_range) { return children_if(t, descended_from_type, search_range); }, "descended_from_type"_a = py::none(), "search_range"_a = nullopt); + + py::class_>(m, "TimedText", py::dynamic_attr()) + .def(py::init([]( + std::string text, + RationalTime in_time, + RationalTime out_time, + TimedTextStyle* style) { + return new TimedText( + text, + in_time, + out_time, + style); + }), + "text"_a = std::string(), + "in_time"_a = RationalTime(), + "out_time"_a = RationalTime(), + "style"_a = nullptr) + .def_property("text", &TimedText::text, &TimedText::set_text) + .def_property_readonly("in_time", &TimedText::in_time) + .def_property_readonly("out_time", &TimedText::out_time) + .def_property("style", &TimedText::style, &TimedText::set_style); } static void define_items_and_compositions(py::module m) { diff --git a/src/py-opentimelineio/opentimelineio/schema/__init__.py b/src/py-opentimelineio/opentimelineio/schema/__init__.py index aa143c58c9..706d42c069 100644 --- a/src/py-opentimelineio/opentimelineio/schema/__init__.py +++ b/src/py-opentimelineio/opentimelineio/schema/__init__.py @@ -40,6 +40,7 @@ MissingReference, SerializableCollection, Stack, + TimedText, TimedTextStyle, Timeline, Track, @@ -64,6 +65,7 @@ marker, serializable_collection, stack, + timed_text, timed_text_style, timeline, track, diff --git a/src/py-opentimelineio/opentimelineio/schema/timed_text.py b/src/py-opentimelineio/opentimelineio/schema/timed_text.py new file mode 100644 index 0000000000..7687286d2d --- /dev/null +++ b/src/py-opentimelineio/opentimelineio/schema/timed_text.py @@ -0,0 +1,32 @@ +from .. core._core_utils import add_method +from .. import _otio + + +@add_method(_otio.TimedText) +def __str__(self): + return ( + 'TimedText(' + '"{}", "{}", "{}", {})' .format( + self.text, + self.in_time, + self.out_time, + self.style + ) + ) + + +@add_method(_otio.TimedText) +def __repr__(self): + return ( + 'TimedText(' + 'text={}, ' + 'in_time={}, ' + 'out_time={}, ' + 'style={}' + ')' .format( + repr(self.text), + repr(self.in_time), + repr(self.out_time), + repr(self.style), + ) + ) \ No newline at end of file From 364559080a012c8b2dfecf168cc23ebebf528d49 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Sat, 3 Oct 2020 11:53:55 +0530 Subject: [PATCH 04/15] Rebase with main --- src/opentimelineio/subtitles.h | 8 ++++ .../otio_serializableObjects.cpp | 42 ++++++++++++++++++ .../opentimelineio/core/_core_utils.py | 1 + .../opentimelineio/schema/__init__.py | 2 + .../opentimelineio/schema/subtitles.py | 44 +++++++++++++++++++ .../opentimelineio/schema/timed_text.py | 2 +- .../opentimelineio/schema/timed_text_style.py | 2 +- 7 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/py-opentimelineio/opentimelineio/schema/subtitles.py diff --git a/src/opentimelineio/subtitles.h b/src/opentimelineio/subtitles.h index 2f8d2b1059..c202c6aa91 100644 --- a/src/opentimelineio/subtitles.h +++ b/src/opentimelineio/subtitles.h @@ -89,6 +89,14 @@ namespace opentimelineio { _display_alignment = display_alignment; } + std::vector>& timed_texts() { + return _timed_texts; + } + + std::vector> const& timed_texts() const { + return _timed_texts; + } + protected: ~Subtitles(); diff --git a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp index 16c2181cf0..b747f87fd0 100644 --- a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp +++ b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp @@ -26,6 +26,7 @@ #include "opentimelineio/timedTextStyle.h" #include "opentimelineio/serializableCollection.h" #include "opentimelineio/stack.h" +#include "opentimelineio/subtitles.h" #include "opentimelineio/unknownSchema.h" #include "otio_utils.h" @@ -43,6 +44,9 @@ using EffectVectorProxy = using TrackVectorProxy = MutableSequencePyAPI>, Track*>; +using TimedTextVectorProxy = + MutableSequencePyAPI>, TimedText*>; + using SOWithMetadata = SerializableObjectWithMetadata; namespace { @@ -380,6 +384,8 @@ static void define_bases2(py::module m) { } static void define_items_and_compositions(py::module m) { + TimedTextVectorProxy::define_py_class(m, "TimedTextVector"); + py::class_>(m, "Item", py::dynamic_attr()) .def(py::init([](std::string name, optional source_range, py::object effects, py::object markers, py::object metadata) { @@ -682,6 +688,42 @@ static void define_items_and_compositions(py::module m) { .def("children_if", [](Timeline* t, py::object descended_from_type, optional const& search_range) { return children_if(t, descended_from_type, search_range); }, "descended_from_type"_a = py::none(), "search_range"_a = nullopt); + + auto subtitles_class = py::class_>(m, "Subtitles", py::dynamic_attr()); + + py::enum_(subtitles_class, "DisplayAlignment", "Attribute used to specify the alignment of block areas in the block progression direction.") + .value("before", Subtitles::DisplayAlignment::before, "") + .value("after", Subtitles::DisplayAlignment::after, "") + .value("center", Subtitles::DisplayAlignment::center, "") + .value("justify", Subtitles::DisplayAlignment::justify, ""); + + subtitles_class + .def(py::init([](float extent_x, float extent_y, float padding_x, float padding_y, + std::string background_color, float background_opacity, Subtitles::DisplayAlignment display_alignment, + py::object timed_texts) { + return new Subtitles(extent_x, extent_y, padding_x, padding_y, + background_color, background_opacity, display_alignment, + py_to_vector(timed_texts)); + }), + "extent_x"_a = 0.f, + "extent_y"_a = 0.f, + "padding_x"_a = 0.f, + "padding_y"_a = 0.f, + "background_color"_a = std::string(), + "background_opacity"_a = 0.f, + "display_alignment"_a = Subtitles::DisplayAlignment::after, + "timed_texts"_a = py::none()) + .def_property("extent_x", &Subtitles::extent_x, &Subtitles::set_extent_x) + .def_property("extent_y", &Subtitles::extent_y, &Subtitles::set_extent_y) + .def_property("padding_x", &Subtitles::padding_x, &Subtitles::set_padding_x) + .def_property("padding_y", &Subtitles::padding_y, &Subtitles::set_padding_y) + .def_property("background_color", &Subtitles::background_color, &Subtitles::set_background_color) + .def_property("background_opacity", &Subtitles::background_opacity, &Subtitles::set_background_opacity) + .def_property("display_alignment", &Subtitles::display_alignment, &Subtitles::set_display_alignment) + .def_property_readonly("timed_texts", [](Subtitles* subtitles) { + return ((TimedTextVectorProxy*) &subtitles->timed_texts()); + }); +>>>>>>> f50246d (Add python bindings for Subtitles) } static void define_effects(py::module m) { diff --git a/src/py-opentimelineio/opentimelineio/core/_core_utils.py b/src/py-opentimelineio/opentimelineio/core/_core_utils.py index 3e35426edd..043b406250 100644 --- a/src/py-opentimelineio/opentimelineio/core/_core_utils.py +++ b/src/py-opentimelineio/opentimelineio/core/_core_utils.py @@ -393,6 +393,7 @@ def __deepcopy__(self, memo=None): _add_mutable_sequence_methods(AnyVector, conversion_func=_value_to_any) _add_mutable_sequence_methods(_otio.MarkerVector) _add_mutable_sequence_methods(_otio.EffectVector) +_add_mutable_sequence_methods(_otio.TimedTextVector) _add_mutable_sequence_methods(_otio.Composition, side_effecting_insertions=True) _add_mutable_sequence_methods(_otio.SerializableCollection) diff --git a/src/py-opentimelineio/opentimelineio/schema/__init__.py b/src/py-opentimelineio/opentimelineio/schema/__init__.py index 706d42c069..6236c453f0 100644 --- a/src/py-opentimelineio/opentimelineio/schema/__init__.py +++ b/src/py-opentimelineio/opentimelineio/schema/__init__.py @@ -40,6 +40,7 @@ MissingReference, SerializableCollection, Stack, + Subtitles, TimedText, TimedTextStyle, Timeline, @@ -65,6 +66,7 @@ marker, serializable_collection, stack, + subtitles, timed_text, timed_text_style, timeline, diff --git a/src/py-opentimelineio/opentimelineio/schema/subtitles.py b/src/py-opentimelineio/opentimelineio/schema/subtitles.py new file mode 100644 index 0000000000..7642225e0b --- /dev/null +++ b/src/py-opentimelineio/opentimelineio/schema/subtitles.py @@ -0,0 +1,44 @@ +from .. core._core_utils import add_method +from .. import _otio + + +@add_method(_otio.Subtitles) +def __str__(self): + return ( + 'Subtitles(' + '{}, {}, {}, {}, "{}", {}, {}, {})' .format( + self.extent_x, + self.extent_y, + self.padding_x, + self.padding_y, + self.background_color, + self.background_opacity, + self.display_alignment, + str(self.timed_texts) + ) + ) + + +@add_method(_otio.Subtitles) +def __repr__(self): + return ( + 'Subtitles(' + 'extent_x={}, ' + 'extent_y={}, ' + 'padding_x={}, ' + 'padding_y={}, ' + 'background_color={}, ' + 'background_opacity={}, ' + 'display_alignment={}, ' + 'timed_texts={} ' + ')' .format( + repr(self.extent_x), + repr(self.extent_y), + repr(self.padding_x), + repr(self.padding_y), + repr(self.background_color), + repr(self.background_opacity), + repr(self.display_alignment), + repr(self.timed_texts) + ) + ) \ No newline at end of file diff --git a/src/py-opentimelineio/opentimelineio/schema/timed_text.py b/src/py-opentimelineio/opentimelineio/schema/timed_text.py index 7687286d2d..66fbaf9506 100644 --- a/src/py-opentimelineio/opentimelineio/schema/timed_text.py +++ b/src/py-opentimelineio/opentimelineio/schema/timed_text.py @@ -6,7 +6,7 @@ def __str__(self): return ( 'TimedText(' - '"{}", "{}", "{}", {})' .format( + '"{}", {}, {}, {})' .format( self.text, self.in_time, self.out_time, diff --git a/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py b/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py index cea92fc9f4..c82e39f618 100644 --- a/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py +++ b/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py @@ -6,7 +6,7 @@ def __str__(self): return ( 'TimedTextStyle(' - '"{}", "{}", "{}", {}, {}, {}, {}, {})' .format( + '"{}", "{}", "{}", {}, {}, {}, {}, "{}")' .format( self.style_id, self.text_alignment, self.text_color, From 9389e2dd8597ab1e8ad2f736388071a8a82b1685 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Sat, 3 Oct 2020 15:22:46 +0530 Subject: [PATCH 05/15] Add newline at end of file --- src/py-opentimelineio/opentimelineio/schema/subtitles.py | 2 +- src/py-opentimelineio/opentimelineio/schema/timed_text.py | 2 +- src/py-opentimelineio/opentimelineio/schema/timed_text_style.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/py-opentimelineio/opentimelineio/schema/subtitles.py b/src/py-opentimelineio/opentimelineio/schema/subtitles.py index 7642225e0b..3d39fda878 100644 --- a/src/py-opentimelineio/opentimelineio/schema/subtitles.py +++ b/src/py-opentimelineio/opentimelineio/schema/subtitles.py @@ -41,4 +41,4 @@ def __repr__(self): repr(self.display_alignment), repr(self.timed_texts) ) - ) \ No newline at end of file + ) diff --git a/src/py-opentimelineio/opentimelineio/schema/timed_text.py b/src/py-opentimelineio/opentimelineio/schema/timed_text.py index 66fbaf9506..ffeb59595f 100644 --- a/src/py-opentimelineio/opentimelineio/schema/timed_text.py +++ b/src/py-opentimelineio/opentimelineio/schema/timed_text.py @@ -29,4 +29,4 @@ def __repr__(self): repr(self.out_time), repr(self.style), ) - ) \ No newline at end of file + ) diff --git a/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py b/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py index c82e39f618..8d84deb18c 100644 --- a/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py +++ b/src/py-opentimelineio/opentimelineio/schema/timed_text_style.py @@ -41,4 +41,4 @@ def __repr__(self): repr(self.text_underline), repr(self.font_family), ) - ) \ No newline at end of file + ) From f054bbe61ad6d5f2693900c2faba1e6fe9ee1dbb Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Sat, 3 Oct 2020 15:52:31 +0530 Subject: [PATCH 06/15] Add schema to typeregistry, use double instead of float and update docs --- docs/tutorials/otio-plugins.md | 34 +++++----- .../otio-serialized-schema-only-fields.md | 39 ++++++++++++ docs/tutorials/otio-serialized-schema.md | 63 +++++++++++++++++++ src/opentimelineio/subtitles.cpp | 10 +-- src/opentimelineio/subtitles.h | 40 ++++++------ src/opentimelineio/timedTextStyle.cpp | 2 +- src/opentimelineio/timedTextStyle.h | 8 +-- src/opentimelineio/typeRegistry.cpp | 6 ++ 8 files changed, 155 insertions(+), 47 deletions(-) diff --git a/docs/tutorials/otio-plugins.md b/docs/tutorials/otio-plugins.md index d750a4b532..3c04350912 100644 --- a/docs/tutorials/otio-plugins.md +++ b/docs/tutorials/otio-plugins.md @@ -15,12 +15,12 @@ file should be regenerated. The manifests describe plugins that are visible to OpenTimelineIO. The core and contrib manifests are listed first, then any user-defined local plugins. -- `opentimelineio/adapters/builtin_adapters.plugin_manifest.json` -- `opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json` +- `opentimelineio\adapters\builtin_adapters.plugin_manifest.json` +- `opentimelineio_contrib\adapters\contrib_adapters.plugin_manifest.json` # Core Plugins -Manifest path: `opentimelineio/adapters/builtin_adapters.plugin_manifest.json` +Manifest path: `opentimelineio\adapters\builtin_adapters.plugin_manifest.json` @@ -41,7 +41,7 @@ adapter. OpenTimelineIO CMX 3600 EDL Adapter ``` -*source*: `opentimelineio/adapters/cmx_3600.py` +*source*: `opentimelineio\adapters\cmx_3600.py` *Supported Features (with arguments)*: @@ -91,7 +91,7 @@ Reads a CMX Edit Decision List (EDL) from a string. OpenTimelineIO Final Cut Pro 7 XML Adapter. ``` -*source*: `opentimelineio/adapters/fcp_xml.py` +*source*: `opentimelineio\adapters\fcp_xml.py` *Supported Features (with arguments)*: @@ -111,7 +111,7 @@ OpenTimelineIO Final Cut Pro 7 XML Adapter. This adapter lets you read and write native .otio files ``` -*source*: `opentimelineio/adapters/otio_json.py` +*source*: `opentimelineio\adapters\otio_json.py` *Supported Features (with arguments)*: @@ -282,7 +282,7 @@ Hooks are the points at which hookscripts will run. Plugins in Contrib are supported by the community and provided as-is. -Manifest path: `opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json` +Manifest path: `opentimelineio_contrib\adapters\contrib_adapters.plugin_manifest.json` @@ -306,7 +306,7 @@ Depending on if/where PyAAF is installed, you may need to set this env var: OTIO_AAF_PYTHON_LIB - should point at the PyAAF module. ``` -*source*: `opentimelineio_contrib/adapters/advanced_authoring_format.py` +*source*: `opentimelineio_contrib\adapters\advanced_authoring_format.py` *Supported Features (with arguments)*: @@ -329,7 +329,7 @@ Depending on if/where PyAAF is installed, you may need to set this env var: OpenTimelineIO Avid Log Exchange (ALE) Adapter ``` -*source*: `opentimelineio_contrib/adapters/ale.py` +*source*: `opentimelineio_contrib\adapters\ale.py` *Supported Features (with arguments)*: @@ -353,7 +353,7 @@ OpenTimelineIO Avid Log Exchange (ALE) Adapter FFMPEG Burnins Adapter ``` -*source*: `opentimelineio_contrib/adapters/burnins.py` +*source*: `opentimelineio_contrib\adapters\burnins.py` *Supported Features (with arguments)*: @@ -375,7 +375,7 @@ required OTIO function hook OpenTimelineIO Final Cut Pro X XML Adapter. ``` -*source*: `opentimelineio_contrib/adapters/fcpx_xml.py` +*source*: `opentimelineio_contrib\adapters\fcpx_xml.py` *Supported Features (with arguments)*: @@ -506,7 +506,7 @@ attributes as defined in sections 4.3.4.2 and 4.3.4.1 of draft-pantos-http-live-streaming, respectively. ``` -*source*: `opentimelineio_contrib/adapters/hls_playlist.py` +*source*: `opentimelineio_contrib\adapters\hls_playlist.py` *Supported Features (with arguments)*: @@ -532,7 +532,7 @@ Adapter entry point for writing. Kdenlive (MLT XML) Adapter. ``` -*source*: `opentimelineio_contrib/adapters/kdenlive.py` +*source*: `opentimelineio_contrib\adapters\kdenlive.py` *Supported Features (with arguments)*: @@ -564,7 +564,7 @@ Write a timeline to Kdenlive project Maya Sequencer Adapter Harness ``` -*source*: `opentimelineio_contrib/adapters/maya_sequencer.py` +*source*: `opentimelineio_contrib\adapters\maya_sequencer.py` *Supported Features (with arguments)*: @@ -585,7 +585,7 @@ Maya Sequencer Adapter Harness RvSession Adapter harness ``` -*source*: `opentimelineio_contrib/adapters/rv.py` +*source*: `opentimelineio_contrib\adapters\rv.py` *Supported Features (with arguments)*: @@ -604,7 +604,7 @@ RvSession Adapter harness OpenTimelineIO GStreamer Editing Services XML Adapter. ``` -*source*: `opentimelineio_contrib/adapters/xges.py` +*source*: `opentimelineio_contrib\adapters\xges.py` *Supported Features (with arguments)*: @@ -660,7 +660,7 @@ schemadef OpenTimelineIO GStreamer Editing Services XML Adapter. ``` -*source*: `opentimelineio_contrib/adapters/xges.py` +*source*: `opentimelineio_contrib\adapters\xges.py` *Serializable Classes*: diff --git a/docs/tutorials/otio-serialized-schema-only-fields.md b/docs/tutorials/otio-serialized-schema-only-fields.md index 945496172e..8e52f316fa 100644 --- a/docs/tutorials/otio-serialized-schema-only-fields.md +++ b/docs/tutorials/otio-serialized-schema-only-fields.md @@ -232,6 +232,23 @@ parameters: - *name* - *source_range* +### Subtitles.1 + +parameters: +- *background_color* +- *background_opacity* +- *display_alignment* +- *effects* +- *extent_x* +- *extent_y* +- *markers* +- *metadata* +- *name* +- *padding_x* +- *padding_y* +- *source_range* +- *timed_texts* + ### TimeEffect.1 parameters: @@ -239,6 +256,28 @@ parameters: - *metadata* - *name* +### TimedText.1 + +parameters: +- *color* +- *marked_range* +- *metadata* +- *name* +- *style* +- *text* + +### TimedTextStyle.1 + +parameters: +- *font_family* +- *style_id* +- *text_alignment* +- *text_bold* +- *text_color* +- *text_italics* +- *text_size* +- *text_underline* + ### Timeline.1 parameters: diff --git a/docs/tutorials/otio-serialized-schema.md b/docs/tutorials/otio-serialized-schema.md index e3028a7204..f74f1b040f 100644 --- a/docs/tutorials/otio-serialized-schema.md +++ b/docs/tutorials/otio-serialized-schema.md @@ -517,6 +517,31 @@ parameters: - *name*: - *source_range*: +### Subtitles.1 + +*full module path*: `opentimelineio.schema.Subtitles` + +*documentation*: + +``` +None +``` + +parameters: +- *background_color*: +- *background_opacity*: +- *display_alignment*: +- *effects*: +- *extent_x*: +- *extent_y*: +- *markers*: +- *metadata*: +- *name*: +- *padding_x*: +- *padding_y*: +- *source_range*: +- *timed_texts*: + ### TimeEffect.1 *full module path*: `opentimelineio.schema.TimeEffect` @@ -532,6 +557,44 @@ parameters: - *metadata*: - *name*: +### TimedText.1 + +*full module path*: `opentimelineio.schema.TimedText` + +*documentation*: + +``` +None +``` + +parameters: +- *color*: +- *marked_range*: +- *metadata*: +- *name*: +- *style*: +- *text*: + +### TimedTextStyle.1 + +*full module path*: `opentimelineio.schema.TimedTextStyle` + +*documentation*: + +``` +None +``` + +parameters: +- *font_family*: +- *style_id*: +- *text_alignment*: +- *text_bold*: +- *text_color*: +- *text_italics*: +- *text_size*: +- *text_underline*: + ### Timeline.1 *full module path*: `opentimelineio.schema.Timeline` diff --git a/src/opentimelineio/subtitles.cpp b/src/opentimelineio/subtitles.cpp index b0d55ef436..0d8dc59470 100644 --- a/src/opentimelineio/subtitles.cpp +++ b/src/opentimelineio/subtitles.cpp @@ -3,12 +3,12 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION { - Subtitles::Subtitles(float extent_x, - float extent_y, - float padding_x, - float padding_y, + Subtitles::Subtitles(double extent_x, + double extent_y, + double padding_x, + double padding_y, std::string background_color, - float background_opacity, + double background_opacity, DisplayAlignment display_alignment, std::vector timed_texts) : Parent(), diff --git a/src/opentimelineio/subtitles.h b/src/opentimelineio/subtitles.h index c202c6aa91..45a7925b98 100644 --- a/src/opentimelineio/subtitles.h +++ b/src/opentimelineio/subtitles.h @@ -22,46 +22,46 @@ namespace opentimelineio { using Parent = Item; - Subtitles(float extent_x = 0.f, - float extent_y = 0.f, - float padding_x = 0.f, - float padding_y = 0.f, + Subtitles(double extent_x = 0.f, + double extent_y = 0.f, + double padding_x = 0.f, + double padding_y = 0.f, std::string background_color = std::string(), - float background_opacity = 0.f, + double background_opacity = 0.f, DisplayAlignment display_alignment = DisplayAlignment::after, std::vector timed_texts = std::vector()); virtual TimeRange available_range(ErrorStatus *error_status) const; - float const extent_x() const { + double const extent_x() const { return _extent_x; } - void set_extent_x(float const extent_x) { + void set_extent_x(double const extent_x) { _extent_x = extent_x; } - float const extent_y() const { + double const extent_y() const { return _extent_y; } - void set_extent_y(float const extent_y) { + void set_extent_y(double const extent_y) { _extent_y = extent_y; } - float const padding_x() const { + double const padding_x() const { return _padding_x; } - void set_padding_x(float const padding_x) { + void set_padding_x(double const padding_x) { _padding_x = padding_x; } - float const padding_y() const { + double const padding_y() const { return _padding_y; } - void set_padding_y(float const padding_y) { + void set_padding_y(double const padding_y) { _padding_y = padding_y; } @@ -73,11 +73,11 @@ namespace opentimelineio { _background_color = background_color; } - float const background_opacity() const { + double const background_opacity() const { return _background_opacity; } - void set_background_opacity(float const background_opacity) { + void set_background_opacity(double const background_opacity) { _background_opacity = background_opacity; } @@ -105,12 +105,12 @@ namespace opentimelineio { virtual void write_to(Writer &) const; private: - float _extent_x; - float _extent_y; - float _padding_x; - float _padding_y; + double _extent_x; + double _extent_y; + double _padding_x; + double _padding_y; std::string _background_color; - float _background_opacity; + double _background_opacity; DisplayAlignment _display_alignment; std::vector> _timed_texts; }; diff --git a/src/opentimelineio/timedTextStyle.cpp b/src/opentimelineio/timedTextStyle.cpp index 8af34f9592..129c80f240 100644 --- a/src/opentimelineio/timedTextStyle.cpp +++ b/src/opentimelineio/timedTextStyle.cpp @@ -6,7 +6,7 @@ namespace opentimelineio { TimedTextStyle::TimedTextStyle(const std::string &style_id, std::string text_alignment, std::string text_color, - float text_size, + double text_size, bool text_bold, bool text_italics, bool text_underline, diff --git a/src/opentimelineio/timedTextStyle.h b/src/opentimelineio/timedTextStyle.h index 5191d62a8b..3b98cabc0a 100644 --- a/src/opentimelineio/timedTextStyle.h +++ b/src/opentimelineio/timedTextStyle.h @@ -27,7 +27,7 @@ namespace opentimelineio { TimedTextStyle(std::string const &style_id = std::string(), std::string text_alignment = TextAlignment::bottom, std::string text_color = Marker::Color::black, - float text_size = 10, + double text_size = 10, bool text_bold = false, bool text_italics = false, bool text_underline = false, @@ -57,11 +57,11 @@ namespace opentimelineio { _text_color = text_color; } - float const text_size() const { + double const text_size() const { return _text_size; } - void set_text_size(float const text_size) { + void set_text_size(double const text_size) { _text_size = text_size; } @@ -108,7 +108,7 @@ namespace opentimelineio { std::string _text_color; std::string _style_id; std::string _text_alignment; - float _text_size; + double _text_size; bool _text_bold; bool _text_italics; bool _text_underline; diff --git a/src/opentimelineio/typeRegistry.cpp b/src/opentimelineio/typeRegistry.cpp index 984b4f946b..3448633028 100644 --- a/src/opentimelineio/typeRegistry.cpp +++ b/src/opentimelineio/typeRegistry.cpp @@ -19,6 +19,9 @@ #include "opentimelineio/serializableObject.h" #include "opentimelineio/serializableObjectWithMetadata.h" #include "opentimelineio/stack.h" +#include "opentimelineio/subtitles.h" +#include "opentimelineio/timedText.h" +#include "opentimelineio/timedTextStyle.h" #include "opentimelineio/timeEffect.h" #include "opentimelineio/timeline.h" #include "opentimelineio/track.h" @@ -51,6 +54,9 @@ TypeRegistry::TypeRegistry() { register_type(); register_type(); register_type(); + register_type(); + register_type(); + register_type(); register_type(); register_type_from_existing_type("Filler", 1, "Gap", nullptr); From eeeae47b277b915b22b3bf6286827439a09bb796 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Sat, 3 Oct 2020 17:14:19 +0530 Subject: [PATCH 07/15] Add otio to srt conversion in adapter --- .../contrib_adapters.plugin_manifest.json | 7 +++ .../opentimelineio_contrib/adapters/srt.py | 59 +++++++++++++++++++ src/opentimelineio/timedText.h | 2 +- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 contrib/opentimelineio_contrib/adapters/srt.py diff --git a/contrib/opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json b/contrib/opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json index cee2946293..7a9478d475 100644 --- a/contrib/opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json +++ b/contrib/opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json @@ -63,6 +63,13 @@ "execution_scope": "in process", "filepath": "kdenlive.py", "suffixes": ["kdenlive"] + }, + { + "OTIO_SCHEMA": "Adapter.1", + "name": "srt", + "execution_scope": "in process", + "filepath": "srt.py", + "suffixes": ["srt"] } ], "schemadefs" : [ diff --git a/contrib/opentimelineio_contrib/adapters/srt.py b/contrib/opentimelineio_contrib/adapters/srt.py new file mode 100644 index 0000000000..ad6d06bac0 --- /dev/null +++ b/contrib/opentimelineio_contrib/adapters/srt.py @@ -0,0 +1,59 @@ +# +# Copyright Contributors to the OpenTimelineIO project +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""SRT Adapter harness""" + +from .. import adapters + +import opentimelineio as otio + + +def timed_text_to_srt_block(timed_text): + in_time_str = otio.opentime.to_time_string(timed_text.in_time) + out_time_str = otio.opentime.to_time_string(timed_text.out_time) + in_time_str_decimal_index = in_time_str.rfind('.') + out_time_str_decimal_index = out_time_str.rfind('.') + in_time_ms = in_time_str[in_time_str_decimal_index + 1:] + out_time_ms = out_time_str[out_time_str_decimal_index + 1:] + in_time_str = in_time_str[0:in_time_str_decimal_index] + ',{:0<3}'.format( + in_time_ms[:3]) + out_time_str = out_time_str[0:out_time_str_decimal_index] + ',{:0<3}'.format( + out_time_ms[:3]) + timed_text_str = in_time_str + ' --> ' + out_time_str + '\n' + timed_text.text + '\n' + return timed_text_str + + +def write_to_string(input_otio): + if (not isinstance(input_otio, otio.schema.Subtitles)): + raise ValueError('Object not of type Subtitles!') + + str_string = '' + block_count = 0 + + for timed_text in input_otio.timed_texts: + block_count += 1 + str_string = str_string + str(block_count) + '\n' + timed_text_to_srt_block( + timed_text) + '\n' + + return str_string diff --git a/src/opentimelineio/timedText.h b/src/opentimelineio/timedText.h index 968d865c54..0980900610 100644 --- a/src/opentimelineio/timedText.h +++ b/src/opentimelineio/timedText.h @@ -42,7 +42,7 @@ namespace opentimelineio { } RationalTime out_time() const { - return marked_range().end_time_inclusive(); + return marked_range().end_time_exclusive(); } protected: From 7e3aa9434830f5daf80a7995493284890aa0adcc Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Wed, 21 Oct 2020 02:07:23 +0530 Subject: [PATCH 08/15] Implement srt adapter and add tests --- .../opentimelineio_contrib/adapters/srt.py | 31 ++++- .../tests/sample_data/srt_example.otio | 122 ++++++++++++++++++ .../tests/sample_data/srt_example.srt | 22 ++++ .../adapters/tests/test_srt_adapter.py | 83 ++++++++++++ docs/tutorials/otio-plugins.md | 54 +++++--- 5 files changed, 290 insertions(+), 22 deletions(-) create mode 100644 contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio create mode 100644 contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.srt create mode 100644 contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py diff --git a/contrib/opentimelineio_contrib/adapters/srt.py b/contrib/opentimelineio_contrib/adapters/srt.py index ad6d06bac0..0cadae7dbe 100644 --- a/contrib/opentimelineio_contrib/adapters/srt.py +++ b/contrib/opentimelineio_contrib/adapters/srt.py @@ -24,8 +24,7 @@ """SRT Adapter harness""" -from .. import adapters - +from itertools import groupby import opentimelineio as otio @@ -40,12 +39,12 @@ def timed_text_to_srt_block(timed_text): in_time_ms[:3]) out_time_str = out_time_str[0:out_time_str_decimal_index] + ',{:0<3}'.format( out_time_ms[:3]) - timed_text_str = in_time_str + ' --> ' + out_time_str + '\n' + timed_text.text + '\n' + timed_text_str = in_time_str + ' --> ' + out_time_str + '\n' + timed_text.text return timed_text_str def write_to_string(input_otio): - if (not isinstance(input_otio, otio.schema.Subtitles)): + if not isinstance(input_otio, otio.schema.Subtitles): raise ValueError('Object not of type Subtitles!') str_string = '' @@ -56,4 +55,26 @@ def write_to_string(input_otio): str_string = str_string + str(block_count) + '\n' + timed_text_to_srt_block( timed_text) + '\n' - return str_string + return str_string.strip() + + +def read_from_file(filepath): + with open(filepath) as f: + subs = [list(g) for b, g in groupby(f, lambda x: bool(x.strip())) if b] + + timed_texts = [] + for sub in subs: + timestamps = sub[1].strip() + start_time_str = timestamps[0:timestamps.find('-')].strip().replace(',', '.') + end_time_str = timestamps[timestamps.find('>') + 1:].strip().replace(',', '.') + start_time = otio.opentime.from_time_string(start_time_str, 24) + end_time = otio.opentime.from_time_string(end_time_str, 24) + content = '' + for text in sub[2:]: + content = content + text + + if len(content.strip()) == 0: + content = '\n' + tt = otio.schema.TimedText(text=content, in_time=start_time, out_time=end_time) + timed_texts.append(tt) + return otio.schema.Subtitles(timed_texts=timed_texts) diff --git a/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio b/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio new file mode 100644 index 0000000000..d176d730ce --- /dev/null +++ b/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio @@ -0,0 +1,122 @@ +{ + "OTIO_SCHEMA": "Subtitles.1", + "metadata": {}, + "name": "", + "source_range": null, + "effects": [], + "markers": [], + "extent_x": 0.0, + "extent_y": 0.0, + "padding_x": 0.0, + "padding_y": 0.0, + "background_color": "", + "background_opacity": 0.0, + "timed_texts": [ + { + "OTIO_SCHEMA": "TimedText.1", + "metadata": {}, + "name": "", + "color": "GREEN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 76.72799999999984 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 805.5840000000001 + } + }, + "text": "Subtitle 1 Line 1\n", + "style": null + }, + { + "OTIO_SCHEMA": "TimedText.1", + "metadata": {}, + "name": "", + "color": "GREEN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 74.25600000000009 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 883.2719999999999 + } + }, + "text": "Subtitle 2 Line 1\nSubtitle 2 Line 2\n", + "style": null + }, + { + "OTIO_SCHEMA": "TimedText.1", + "metadata": {}, + "name": "", + "color": "GREEN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 51.048 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 958.5360000000001 + } + }, + "text": "Subtitle 3 Line 1\nSubtitle 3 Line 2\nSubtitle 3 Line 3\n", + "style": null + }, + { + "OTIO_SCHEMA": "TimedText.1", + "metadata": {}, + "name": "", + "color": "GREEN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 50.2320000000002 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 1010.5919999999999 + } + }, + "text": "\n", + "style": null + }, + { + "OTIO_SCHEMA": "TimedText.1", + "metadata": {}, + "name": "", + "color": "GREEN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 60.59999999999991 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 1061.856 + } + }, + "text": "Subtitle 5 Line 1", + "style": null + } + ], + "display_alignment": "after" +} \ No newline at end of file diff --git a/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.srt b/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.srt new file mode 100644 index 0000000000..0113b7d86b --- /dev/null +++ b/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.srt @@ -0,0 +1,22 @@ +1 +00:00:33,566 --> 00:00:36,763 +Subtitle 1 Line 1 + +2 +00:00:36,803 --> 00:00:39,897 +Subtitle 2 Line 1 +Subtitle 2 Line 2 + +3 +00:00:39,939 --> 00:00:42,066 +Subtitle 3 Line 1 +Subtitle 3 Line 2 +Subtitle 3 Line 3 + +4 +00:00:42,108 --> 00:00:44,201 + + +5 +00:00:44,244 --> 00:00:46,769 +Subtitle 5 Line 1 \ No newline at end of file diff --git a/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py new file mode 100644 index 0000000000..848ad51081 --- /dev/null +++ b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py @@ -0,0 +1,83 @@ +# +# Copyright Contributors to the OpenTimelineIO project +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Unit tests for the srt file adapter""" + +import unittest +import os +import tempfile + +import opentimelineio as otio + +OTIO_SAMPLE_DATA_DIR = os.path.join( + os.path.dirname(__file__), + "sample_data" +) +SRT_EXAMPLE_PATH = os.path.join(OTIO_SAMPLE_DATA_DIR, "srt_example.srt") +SRT_OTIO_EXAMPLE_PATH = os.path.join(OTIO_SAMPLE_DATA_DIR, + "srt_example.otio") + + +class SRTTest(unittest.TestCase): + def setUp(self): + fd, self.tmp_path = tempfile.mkstemp(suffix=".srt", text=True) + os.close(fd) + + fd, self.tmp_path_otio = tempfile.mkstemp(suffix=".otio", text=True) + os.close(fd) + + def tearDown(self): + os.unlink(self.tmp_path) + + def test_srt_read(self): + st = otio.adapters.read_from_file(SRT_EXAMPLE_PATH) + + otio.adapters.write_to_file(st, self.tmp_path) + + with open(self.tmp_path) as f: + test_data = f.read().strip() + + with open(SRT_EXAMPLE_PATH) as f: + baseline_data = f.read().strip() + + self.maxDiff = None + self.assertMultiLineEqual(baseline_data, test_data) + + def test_otio(self): + st = otio.adapters.read_from_file(SRT_EXAMPLE_PATH) + + otio.adapters.write_to_file(st, self.tmp_path_otio) + + with open(self.tmp_path_otio) as f: + test_data = f.read() + + with open(SRT_OTIO_EXAMPLE_PATH) as f: + baseline_data = f.read() + + self.maxDiff = None + self.assertMultiLineEqual(baseline_data, test_data) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/tutorials/otio-plugins.md b/docs/tutorials/otio-plugins.md index 3c04350912..db7baa8d7a 100644 --- a/docs/tutorials/otio-plugins.md +++ b/docs/tutorials/otio-plugins.md @@ -15,12 +15,12 @@ file should be regenerated. The manifests describe plugins that are visible to OpenTimelineIO. The core and contrib manifests are listed first, then any user-defined local plugins. -- `opentimelineio\adapters\builtin_adapters.plugin_manifest.json` -- `opentimelineio_contrib\adapters\contrib_adapters.plugin_manifest.json` +- `opentimelineio/adapters/builtin_adapters.plugin_manifest.json` +- `opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json` # Core Plugins -Manifest path: `opentimelineio\adapters\builtin_adapters.plugin_manifest.json` +Manifest path: `opentimelineio/adapters/builtin_adapters.plugin_manifest.json` @@ -41,7 +41,7 @@ adapter. OpenTimelineIO CMX 3600 EDL Adapter ``` -*source*: `opentimelineio\adapters\cmx_3600.py` +*source*: `opentimelineio/adapters/cmx_3600.py` *Supported Features (with arguments)*: @@ -91,7 +91,7 @@ Reads a CMX Edit Decision List (EDL) from a string. OpenTimelineIO Final Cut Pro 7 XML Adapter. ``` -*source*: `opentimelineio\adapters\fcp_xml.py` +*source*: `opentimelineio/adapters/fcp_xml.py` *Supported Features (with arguments)*: @@ -111,7 +111,7 @@ OpenTimelineIO Final Cut Pro 7 XML Adapter. This adapter lets you read and write native .otio files ``` -*source*: `opentimelineio\adapters\otio_json.py` +*source*: `opentimelineio/adapters/otio_json.py` *Supported Features (with arguments)*: @@ -282,7 +282,7 @@ Hooks are the points at which hookscripts will run. Plugins in Contrib are supported by the community and provided as-is. -Manifest path: `opentimelineio_contrib\adapters\contrib_adapters.plugin_manifest.json` +Manifest path: `opentimelineio_contrib/adapters/contrib_adapters.plugin_manifest.json` @@ -306,7 +306,7 @@ Depending on if/where PyAAF is installed, you may need to set this env var: OTIO_AAF_PYTHON_LIB - should point at the PyAAF module. ``` -*source*: `opentimelineio_contrib\adapters\advanced_authoring_format.py` +*source*: `opentimelineio_contrib/adapters/advanced_authoring_format.py` *Supported Features (with arguments)*: @@ -329,7 +329,7 @@ Depending on if/where PyAAF is installed, you may need to set this env var: OpenTimelineIO Avid Log Exchange (ALE) Adapter ``` -*source*: `opentimelineio_contrib\adapters\ale.py` +*source*: `opentimelineio_contrib/adapters/ale.py` *Supported Features (with arguments)*: @@ -353,7 +353,7 @@ OpenTimelineIO Avid Log Exchange (ALE) Adapter FFMPEG Burnins Adapter ``` -*source*: `opentimelineio_contrib\adapters\burnins.py` +*source*: `opentimelineio_contrib/adapters/burnins.py` *Supported Features (with arguments)*: @@ -375,7 +375,7 @@ required OTIO function hook OpenTimelineIO Final Cut Pro X XML Adapter. ``` -*source*: `opentimelineio_contrib\adapters\fcpx_xml.py` +*source*: `opentimelineio_contrib/adapters/fcpx_xml.py` *Supported Features (with arguments)*: @@ -506,7 +506,7 @@ attributes as defined in sections 4.3.4.2 and 4.3.4.1 of draft-pantos-http-live-streaming, respectively. ``` -*source*: `opentimelineio_contrib\adapters\hls_playlist.py` +*source*: `opentimelineio_contrib/adapters/hls_playlist.py` *Supported Features (with arguments)*: @@ -532,7 +532,7 @@ Adapter entry point for writing. Kdenlive (MLT XML) Adapter. ``` -*source*: `opentimelineio_contrib\adapters\kdenlive.py` +*source*: `opentimelineio_contrib/adapters/kdenlive.py` *Supported Features (with arguments)*: @@ -564,7 +564,7 @@ Write a timeline to Kdenlive project Maya Sequencer Adapter Harness ``` -*source*: `opentimelineio_contrib\adapters\maya_sequencer.py` +*source*: `opentimelineio_contrib/adapters/maya_sequencer.py` *Supported Features (with arguments)*: @@ -585,7 +585,7 @@ Maya Sequencer Adapter Harness RvSession Adapter harness ``` -*source*: `opentimelineio_contrib\adapters\rv.py` +*source*: `opentimelineio_contrib/adapters/rv.py` *Supported Features (with arguments)*: @@ -598,13 +598,33 @@ RvSession Adapter harness +### srt + +``` +SRT Adapter harness +``` + +*source*: `opentimelineio_contrib/adapters/srt.py` + + +*Supported Features (with arguments)*: + +- read_from_file: + - filepath +- write_to_string: + - input_otio + + + + + ### xges ``` OpenTimelineIO GStreamer Editing Services XML Adapter. ``` -*source*: `opentimelineio_contrib\adapters\xges.py` +*source*: `opentimelineio_contrib/adapters/xges.py` *Supported Features (with arguments)*: @@ -660,7 +680,7 @@ schemadef OpenTimelineIO GStreamer Editing Services XML Adapter. ``` -*source*: `opentimelineio_contrib\adapters\xges.py` +*source*: `opentimelineio_contrib/adapters/xges.py` *Serializable Classes*: From 2e50037b8bde74023921b409457c6bd38129c75a Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Wed, 21 Oct 2020 13:28:39 +0530 Subject: [PATCH 09/15] Use io.open() instead of os.open() --- contrib/opentimelineio_contrib/adapters/srt.py | 3 ++- .../adapters/tests/test_srt_adapter.py | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/contrib/opentimelineio_contrib/adapters/srt.py b/contrib/opentimelineio_contrib/adapters/srt.py index 0cadae7dbe..52fa554ba0 100644 --- a/contrib/opentimelineio_contrib/adapters/srt.py +++ b/contrib/opentimelineio_contrib/adapters/srt.py @@ -26,6 +26,7 @@ from itertools import groupby import opentimelineio as otio +import io def timed_text_to_srt_block(timed_text): @@ -59,7 +60,7 @@ def write_to_string(input_otio): def read_from_file(filepath): - with open(filepath) as f: + with io.open(filepath) as f: subs = [list(g) for b, g in groupby(f, lambda x: bool(x.strip())) if b] timed_texts = [] diff --git a/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py index 848ad51081..d8fdf10bd7 100644 --- a/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py +++ b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py @@ -27,6 +27,7 @@ import unittest import os import tempfile +import io import opentimelineio as otio @@ -40,6 +41,7 @@ class SRTTest(unittest.TestCase): + maxDiff = None def setUp(self): fd, self.tmp_path = tempfile.mkstemp(suffix=".srt", text=True) os.close(fd) @@ -55,10 +57,10 @@ def test_srt_read(self): otio.adapters.write_to_file(st, self.tmp_path) - with open(self.tmp_path) as f: + with io.open(self.tmp_path) as f: test_data = f.read().strip() - with open(SRT_EXAMPLE_PATH) as f: + with io.open(self.tmp_path) as f: baseline_data = f.read().strip() self.maxDiff = None @@ -69,12 +71,12 @@ def test_otio(self): otio.adapters.write_to_file(st, self.tmp_path_otio) - with open(self.tmp_path_otio) as f: + with io.open(self.tmp_path_otio) as f: test_data = f.read() - with open(SRT_OTIO_EXAMPLE_PATH) as f: + with io.open(SRT_OTIO_EXAMPLE_PATH) as f: baseline_data = f.read() - + self.maxDiff = None self.assertMultiLineEqual(baseline_data, test_data) From 4f2d8bb1b983ea9332c95325b9e2f6a7ad8110e8 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Wed, 21 Oct 2020 13:34:22 +0530 Subject: [PATCH 10/15] formatting --- .../adapters/tests/test_srt_adapter.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py index d8fdf10bd7..d7f2f6c66a 100644 --- a/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py +++ b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py @@ -42,6 +42,7 @@ class SRTTest(unittest.TestCase): maxDiff = None + def setUp(self): fd, self.tmp_path = tempfile.mkstemp(suffix=".srt", text=True) os.close(fd) @@ -54,7 +55,6 @@ def tearDown(self): def test_srt_read(self): st = otio.adapters.read_from_file(SRT_EXAMPLE_PATH) - otio.adapters.write_to_file(st, self.tmp_path) with io.open(self.tmp_path) as f: @@ -68,7 +68,6 @@ def test_srt_read(self): def test_otio(self): st = otio.adapters.read_from_file(SRT_EXAMPLE_PATH) - otio.adapters.write_to_file(st, self.tmp_path_otio) with io.open(self.tmp_path_otio) as f: @@ -76,7 +75,7 @@ def test_otio(self): with io.open(SRT_OTIO_EXAMPLE_PATH) as f: baseline_data = f.read() - + self.maxDiff = None self.assertMultiLineEqual(baseline_data, test_data) From 9917c492917d688cb022491db526ab2088270e17 Mon Sep 17 00:00:00 2001 From: Karthik Ramesh Iyer Date: Tue, 1 Jun 2021 00:26:06 +0530 Subject: [PATCH 11/15] Extract xy pos --- contrib/opentimelineio_contrib/adapters/srt.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/contrib/opentimelineio_contrib/adapters/srt.py b/contrib/opentimelineio_contrib/adapters/srt.py index 52fa554ba0..5a8924329c 100644 --- a/contrib/opentimelineio_contrib/adapters/srt.py +++ b/contrib/opentimelineio_contrib/adapters/srt.py @@ -66,10 +66,22 @@ def read_from_file(filepath): timed_texts = [] for sub in subs: timestamps = sub[1].strip() - start_time_str = timestamps[0:timestamps.find('-')].strip().replace(',', '.') - end_time_str = timestamps[timestamps.find('>') + 1:].strip().replace(',', '.') + timestamps_data = timestamps.split() + # start_time_str = timestamps[0:timestamps.find('-')].strip().replace(',', '.') + start_time_str = timestamps_data[0].strip().replace(',', '.') + # end_time_str = timestamps[timestamps.find('>') + 1:].strip().replace(',', '.') + end_time_str = timestamps_data[2].strip().replace(',', '.') start_time = otio.opentime.from_time_string(start_time_str, 24) end_time = otio.opentime.from_time_string(end_time_str, 24) + xpos1 = None + ypos1 = None + xpos2 = None + ypos2 = None + if (len(timestamps_data) == 7): + xpos1 = float(timestamps_data[3][3:]) + xpos2 = float(timestamps_data[4][3:]) + ypos1 = float(timestamps_data[5][3:]) + ypos2 = float(timestamps_data[6][3:]) content = '' for text in sub[2:]: content = content + text From 622f231ffa9d7a2fccf66bf36e79c83c5d5b1c1f Mon Sep 17 00:00:00 2001 From: Karthik Iyer Date: Sun, 1 Aug 2021 13:40:12 +0530 Subject: [PATCH 12/15] Fix rebase --- .../opentimelineio-bindings/otio_serializableObjects.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp index b747f87fd0..a669d52b01 100644 --- a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp +++ b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp @@ -723,7 +723,6 @@ static void define_items_and_compositions(py::module m) { .def_property_readonly("timed_texts", [](Subtitles* subtitles) { return ((TimedTextVectorProxy*) &subtitles->timed_texts()); }); ->>>>>>> f50246d (Add python bindings for Subtitles) } static void define_effects(py::module m) { From 61dbc973917ace97bc87b4bc10110c8b8a95647d Mon Sep 17 00:00:00 2001 From: Karthik Iyer Date: Sun, 1 Aug 2021 21:21:14 +0530 Subject: [PATCH 13/15] Store vector of string and styleIDs in TimedText --- .../opentimelineio_contrib/adapters/srt.py | 11 +++-- .../tests/sample_data/srt_example.otio | 40 ++++++++++++++----- .../adapters/tests/test_srt_adapter.py | 14 +++---- .../otio-serialized-schema-only-fields.md | 1 - docs/tutorials/otio-serialized-schema.md | 1 - src/opentimelineio/timedText.cpp | 18 ++++----- src/opentimelineio/timedText.h | 29 ++++++-------- .../otio_serializableObjects.cpp | 23 ++++------- .../opentimelineio/schema/timed_text.py | 12 +++--- 9 files changed, 77 insertions(+), 72 deletions(-) diff --git a/contrib/opentimelineio_contrib/adapters/srt.py b/contrib/opentimelineio_contrib/adapters/srt.py index 5a8924329c..2b26da541f 100644 --- a/contrib/opentimelineio_contrib/adapters/srt.py +++ b/contrib/opentimelineio_contrib/adapters/srt.py @@ -40,7 +40,10 @@ def timed_text_to_srt_block(timed_text): in_time_ms[:3]) out_time_str = out_time_str[0:out_time_str_decimal_index] + ',{:0<3}'.format( out_time_ms[:3]) - timed_text_str = in_time_str + ' --> ' + out_time_str + '\n' + timed_text.text + text = '' + for caption in timed_text.texts: + text = text + caption + timed_text_str = in_time_str + ' --> ' + out_time_str + '\n' + text return timed_text_str @@ -64,12 +67,11 @@ def read_from_file(filepath): subs = [list(g) for b, g in groupby(f, lambda x: bool(x.strip())) if b] timed_texts = [] + for sub in subs: timestamps = sub[1].strip() timestamps_data = timestamps.split() - # start_time_str = timestamps[0:timestamps.find('-')].strip().replace(',', '.') start_time_str = timestamps_data[0].strip().replace(',', '.') - # end_time_str = timestamps[timestamps.find('>') + 1:].strip().replace(',', '.') end_time_str = timestamps_data[2].strip().replace(',', '.') start_time = otio.opentime.from_time_string(start_time_str, 24) end_time = otio.opentime.from_time_string(end_time_str, 24) @@ -88,6 +90,7 @@ def read_from_file(filepath): if len(content.strip()) == 0: content = '\n' - tt = otio.schema.TimedText(text=content, in_time=start_time, out_time=end_time) + tt = otio.schema.TimedText(in_time=start_time, out_time=end_time) + tt.add_text(text=content) timed_texts.append(tt) return otio.schema.Subtitles(timed_texts=timed_texts) diff --git a/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio b/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio index d176d730ce..cdc3a54d96 100644 --- a/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio +++ b/contrib/opentimelineio_contrib/adapters/tests/sample_data/srt_example.otio @@ -30,8 +30,12 @@ "value": 805.5840000000001 } }, - "text": "Subtitle 1 Line 1\n", - "style": null + "texts": [ + "Subtitle 1 Line 1\n" + ], + "style_ids": [ + "" + ] }, { "OTIO_SCHEMA": "TimedText.1", @@ -51,8 +55,12 @@ "value": 883.2719999999999 } }, - "text": "Subtitle 2 Line 1\nSubtitle 2 Line 2\n", - "style": null + "texts": [ + "Subtitle 2 Line 1\nSubtitle 2 Line 2\n" + ], + "style_ids": [ + "" + ] }, { "OTIO_SCHEMA": "TimedText.1", @@ -72,8 +80,12 @@ "value": 958.5360000000001 } }, - "text": "Subtitle 3 Line 1\nSubtitle 3 Line 2\nSubtitle 3 Line 3\n", - "style": null + "texts": [ + "Subtitle 3 Line 1\nSubtitle 3 Line 2\nSubtitle 3 Line 3\n" + ], + "style_ids": [ + "" + ] }, { "OTIO_SCHEMA": "TimedText.1", @@ -93,8 +105,12 @@ "value": 1010.5919999999999 } }, - "text": "\n", - "style": null + "texts": [ + "\n" + ], + "style_ids": [ + "" + ] }, { "OTIO_SCHEMA": "TimedText.1", @@ -114,8 +130,12 @@ "value": 1061.856 } }, - "text": "Subtitle 5 Line 1", - "style": null + "texts": [ + "Subtitle 5 Line 1" + ], + "style_ids": [ + "" + ] } ], "display_alignment": "after" diff --git a/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py index d7f2f6c66a..9b3f6bbcde 100644 --- a/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py +++ b/contrib/opentimelineio_contrib/adapters/tests/test_srt_adapter.py @@ -55,25 +55,25 @@ def tearDown(self): def test_srt_read(self): st = otio.adapters.read_from_file(SRT_EXAMPLE_PATH) - otio.adapters.write_to_file(st, self.tmp_path) + otio.adapters.write_to_file(st, self.tmp_path_otio) - with io.open(self.tmp_path) as f: + with io.open(self.tmp_path_otio) as f: test_data = f.read().strip() - with io.open(self.tmp_path) as f: + with io.open(SRT_OTIO_EXAMPLE_PATH) as f: baseline_data = f.read().strip() self.maxDiff = None self.assertMultiLineEqual(baseline_data, test_data) def test_otio(self): - st = otio.adapters.read_from_file(SRT_EXAMPLE_PATH) - otio.adapters.write_to_file(st, self.tmp_path_otio) + st = otio.adapters.read_from_file(SRT_OTIO_EXAMPLE_PATH) + otio.adapters.write_to_file(st, self.tmp_path) - with io.open(self.tmp_path_otio) as f: + with io.open(self.tmp_path) as f: test_data = f.read() - with io.open(SRT_OTIO_EXAMPLE_PATH) as f: + with io.open(SRT_EXAMPLE_PATH) as f: baseline_data = f.read() self.maxDiff = None diff --git a/docs/tutorials/otio-serialized-schema-only-fields.md b/docs/tutorials/otio-serialized-schema-only-fields.md index 8e52f316fa..b6b94ea25f 100644 --- a/docs/tutorials/otio-serialized-schema-only-fields.md +++ b/docs/tutorials/otio-serialized-schema-only-fields.md @@ -263,7 +263,6 @@ parameters: - *marked_range* - *metadata* - *name* -- *style* - *text* ### TimedTextStyle.1 diff --git a/docs/tutorials/otio-serialized-schema.md b/docs/tutorials/otio-serialized-schema.md index f74f1b040f..22864a41a6 100644 --- a/docs/tutorials/otio-serialized-schema.md +++ b/docs/tutorials/otio-serialized-schema.md @@ -572,7 +572,6 @@ parameters: - *marked_range*: - *metadata*: - *name*: -- *style*: - *text*: ### TimedTextStyle.1 diff --git a/src/opentimelineio/timedText.cpp b/src/opentimelineio/timedText.cpp index 211be3f36f..ace3b8fb0d 100644 --- a/src/opentimelineio/timedText.cpp +++ b/src/opentimelineio/timedText.cpp @@ -3,28 +3,24 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION { - TimedText::TimedText(std::string const &text, - RationalTime const &in_time, - RationalTime const &out_time, - TimedTextStyle *style) - : Parent(), - _text(text), - _style(style) { + TimedText::TimedText(RationalTime const &in_time, + RationalTime const &out_time) + : Parent() { set_marked_range(TimeRange::range_from_start_end_time(in_time, out_time)); } TimedText::~TimedText() {} bool TimedText::read_from(Reader &reader) { - return reader.read("text", &_text) && - reader.read("style", &_style) && + return reader.read("texts", &_texts) && + reader.read("style_ids", &_styleIDs) && Parent::read_from(reader); } void TimedText::write_to(Writer &writer) const { Parent::write_to(writer); - writer.write("text", _text); - writer.write("style", _style); + writer.write("texts", _texts); + writer.write("style_ids", _styleIDs); } } diff --git a/src/opentimelineio/timedText.h b/src/opentimelineio/timedText.h index 0980900610..9030a75351 100644 --- a/src/opentimelineio/timedText.h +++ b/src/opentimelineio/timedText.h @@ -16,28 +16,23 @@ namespace opentimelineio { using Parent = Marker; - TimedText(std::string const &text = std::string(), - RationalTime const &in_time = RationalTime(), - RationalTime const &out_time = RationalTime(), - TimedTextStyle *style = new TimedTextStyle()); + explicit TimedText(RationalTime const &in_time = RationalTime(), + RationalTime const &out_time = RationalTime()); - std::string const &text() const { - return _text; + std::vector const &texts() const { + return _texts; } - void set_text(std::string const &text) { - _text = text; + std::vector const &styleIDs() const { + return _texts; } - TimedTextStyle* style() const { - return _style.value; + void add_text(std::string const &text, std::string const &styleID = "") { + _texts.emplace_back(text); + _styleIDs.emplace_back(styleID); } - void set_style(TimedTextStyle *style) { - _style = style; - } - - RationalTime const in_time() const { + RationalTime in_time() const { return marked_range().start_time(); } @@ -53,8 +48,8 @@ namespace opentimelineio { virtual void write_to(Writer &) const; private: - std::string _text; - Retainer _style; + std::vector _texts; + std::vector _styleIDs; }; } diff --git a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp index a669d52b01..078fe595b4 100644 --- a/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp +++ b/src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp @@ -362,25 +362,18 @@ static void define_bases2(py::module m) { }, "descended_from_type"_a = py::none(), "search_range"_a = nullopt); py::class_>(m, "TimedText", py::dynamic_attr()) - .def(py::init([]( - std::string text, - RationalTime in_time, - RationalTime out_time, - TimedTextStyle* style) { - return new TimedText( - text, - in_time, - out_time, - style); + .def(py::init([](RationalTime in_time, RationalTime out_time) { + return new TimedText(in_time, out_time); }), - "text"_a = std::string(), "in_time"_a = RationalTime(), - "out_time"_a = RationalTime(), - "style"_a = nullptr) - .def_property("text", &TimedText::text, &TimedText::set_text) + "out_time"_a = RationalTime()) .def_property_readonly("in_time", &TimedText::in_time) .def_property_readonly("out_time", &TimedText::out_time) - .def_property("style", &TimedText::style, &TimedText::set_style); + .def_property_readonly("texts", &TimedText::texts) + .def_property_readonly("style_ids", &TimedText::styleIDs) + .def("add_text", [](TimedText* timedText, const std::string& text, const std::string& styleID){ + timedText->add_text(text, styleID); + }, "text"_a, "styleID"_a = ""); } static void define_items_and_compositions(py::module m) { diff --git a/src/py-opentimelineio/opentimelineio/schema/timed_text.py b/src/py-opentimelineio/opentimelineio/schema/timed_text.py index ffeb59595f..b262ea6d27 100644 --- a/src/py-opentimelineio/opentimelineio/schema/timed_text.py +++ b/src/py-opentimelineio/opentimelineio/schema/timed_text.py @@ -7,10 +7,10 @@ def __str__(self): return ( 'TimedText(' '"{}", {}, {}, {})' .format( - self.text, self.in_time, self.out_time, - self.style + self.texts, + self.style_ids ) ) @@ -19,14 +19,14 @@ def __str__(self): def __repr__(self): return ( 'TimedText(' - 'text={}, ' 'in_time={}, ' 'out_time={}, ' - 'style={}' + 'texts={}, ' + 'style_ids={}' ')' .format( - repr(self.text), repr(self.in_time), repr(self.out_time), - repr(self.style), + repr(self.texts), + repr(self.style_ids) ) ) From 183eb9b82b13ef20836bc178a8dbb501fef8b40e Mon Sep 17 00:00:00 2001 From: Karthik Iyer Date: Tue, 3 Aug 2021 23:19:50 +0530 Subject: [PATCH 14/15] Implement SRT style parser --- .../opentimelineio_contrib/adapters/srt.py | 148 +++++++++++++++++- 1 file changed, 142 insertions(+), 6 deletions(-) diff --git a/contrib/opentimelineio_contrib/adapters/srt.py b/contrib/opentimelineio_contrib/adapters/srt.py index 2b26da541f..08d8f3838e 100644 --- a/contrib/opentimelineio_contrib/adapters/srt.py +++ b/contrib/opentimelineio_contrib/adapters/srt.py @@ -25,10 +25,124 @@ """SRT Adapter harness""" from itertools import groupby +from html.parser import HTMLParser +from queue import LifoQueue import opentimelineio as otio import io +class SRTStyleParser(HTMLParser): + class StyleState: + + def __init__(self): + self.italics = False + self.bold = False + self.underline = False + self.strikethrough = False + self.font_size = None + self.font_color = None + self.font_face = None + + def clear_state(self): + self.italics = False + self.bold = False + self.underline = False + self.strikethrough = False + self.font_size = None + self.font_color = None + self.font_face = None + + def set_font_size(self, value): + self.font_size = value + + def set_font_face(self, value): + self.font_face = value + + def set_font_color(self, value): + self.font_color = value + + def __repr__(self): + return 'StyleState(' \ + 'italics=' + str(self.italics) + \ + ',bold=' + str(self.bold) + \ + ',underline=' + str(self.underline) + \ + ',strikethrough=' + str(self.strikethrough) + \ + ',fontSize=' + str(self.font_size) + \ + ',fontColor=' + str(self.font_color) + \ + ',fontFace=' + str(self.font_face) + \ + ')' + + def __init__(self): + super().__init__() + self.tagStack = LifoQueue() + self.styleState = self.StyleState() + self.dataState = "" + self.dataList = [] + self.TAG_FUNCTION_MAP = { + 'b': self._process_bold_tag, + 'u': self._process_underline_tag, + 'i': self._process_italics_tag, + 's': self._process_strikethrough_tag, + 'font': self._process_font_tag + } + + def _process_tag(self, tag_attrs_tuple): + if tag_attrs_tuple[0] in self.TAG_FUNCTION_MAP: + self.TAG_FUNCTION_MAP[tag_attrs_tuple[0]](tag_attrs_tuple[1]) + + def _process_bold_tag(self, tag_attrs): + self.styleState.bold = True + + def _process_underline_tag(self, tag_attrs): + self.styleState.underline = True + + def _process_italics_tag(self, tag_attrs): + self.styleState.italics = True + + def _process_strikethrough_tag(self, tag_attrs): + self.styleState.strikethrough = True + + def _process_font_tag(self, tag_attrs): + FONT_ATTR_MAP = { + 'color': self.styleState.set_font_color, + 'face': self.styleState.set_font_face, + 'size': self.styleState.set_font_size, + } + for attribute, value in tag_attrs: + if attribute in FONT_ATTR_MAP: + FONT_ATTR_MAP[attribute](value) + + def parse_srt(self, srt_string): + self.styleState.clear_state() + self.feed(srt_string) + return self.dataList + + def handle_starttag(self, tag, attrs): + attr_list = [] + for attr in attrs: + attr_list.append(attr) + self.tagStack.put((tag, attr_list)) + + def handle_endtag(self, tag): + topTag = self.tagStack.get() + if topTag[0] != tag: + raise Exception('Invalid HTML') + self._process_tag(topTag) + if self.tagStack.empty(): + self.dataList.append((self.dataState, self.styleState)) + self.styleState = self.StyleState() + self.dataState = '' + + def handle_data(self, data): + if self.tagStack.empty(): + self.dataList.append((data, None)) + else: + self.dataState = data + + def error(self, message): + pass + + def timed_text_to_srt_block(timed_text): in_time_str = otio.opentime.to_time_string(timed_text.in_time) out_time_str = otio.opentime.to_time_string(timed_text.out_time) @@ -68,6 +182,9 @@ def read_from_file(filepath): timed_texts = [] + style_id_count = 0 + styles_map = {} + style_parser = SRTStyleParser() for sub in subs: timestamps = sub[1].strip() timestamps_data = timestamps.split() @@ -84,13 +201,32 @@ def read_from_file(filepath): xpos2 = float(timestamps_data[4][3:]) ypos1 = float(timestamps_data[5][3:]) ypos2 = float(timestamps_data[6][3:]) - content = '' + subtitle_text = '' for text in sub[2:]: - content = content + text + subtitle_text = subtitle_text + text - if len(content.strip()) == 0: - content = '\n' + if len(subtitle_text.strip()) == 0: + subtitle_text = '\n' + subtitle_data = style_parser.parse_srt(subtitle_text) tt = otio.schema.TimedText(in_time=start_time, out_time=end_time) - tt.add_text(text=content) + for data in subtitle_data: + style_id = '' + if data[1] is not None: + style_id_count += 1 + style = otio.schema.TimedTextStyle(style_id='style' + str(style_id_count), + text_color=data[1].font_color, + text_size=float(data[1].font_size), + text_bold=data[1].bold, + text_italics=data[1].italics, + text_underline=data[1].underline, + font_family=data[1].font_face) + style_id = str(style_id_count) + styles_map.add(style_id, style) + tt.add_text(text=data[0], styleID=style_id) timed_texts.append(tt) - return otio.schema.Subtitles(timed_texts=timed_texts) + subtitles = otio.schema.Subtitles(timed_texts=timed_texts) + track = otio.schema.Track(name="Subtitles Track") + timeline = otio.schema.Timeline("SRT Timeline", tracks=[track]) + timeline.metadata['styles'] = styles_map + timeline.tracks[0].append(subtitles) + return timeline From 0a64d10c36aeedb896c112ecaf13601b4fbc9b95 Mon Sep 17 00:00:00 2001 From: Karthik Iyer Date: Wed, 4 Aug 2021 22:12:35 +0530 Subject: [PATCH 15/15] Fix default values --- contrib/opentimelineio_contrib/adapters/srt.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/opentimelineio_contrib/adapters/srt.py b/contrib/opentimelineio_contrib/adapters/srt.py index 08d8f3838e..c77a92e56c 100644 --- a/contrib/opentimelineio_contrib/adapters/srt.py +++ b/contrib/opentimelineio_contrib/adapters/srt.py @@ -39,18 +39,18 @@ def __init__(self): self.bold = False self.underline = False self.strikethrough = False - self.font_size = None - self.font_color = None - self.font_face = None + self.font_size = '10' + self.font_color = 'BLACK' + self.font_face = '' def clear_state(self): self.italics = False self.bold = False self.underline = False self.strikethrough = False - self.font_size = None - self.font_color = None - self.font_face = None + self.font_size = '' + self.font_color = 'BLACK' + self.font_face = '' def set_font_size(self, value): self.font_size = value @@ -221,7 +221,7 @@ def read_from_file(filepath): text_underline=data[1].underline, font_family=data[1].font_face) style_id = str(style_id_count) - styles_map.add(style_id, style) + styles_map[style_id] = style tt.add_text(text=data[0], styleID=style_id) timed_texts.append(tt) subtitles = otio.schema.Subtitles(timed_texts=timed_texts)