From d8f62f1237485cf83059727597131504a03a3730 Mon Sep 17 00:00:00 2001 From: Chris Tankersley Date: Fri, 15 Aug 2025 23:06:33 -0400 Subject: [PATCH] feat: Add bidirectional flag --- opentok/opentok.py | 5 +++++ opentok/version.py | 2 +- opentok/websocket_audio_connection.py | 1 + tests/test_audio_connector.py | 32 +++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/opentok/opentok.py b/opentok/opentok.py index 5f5adbf..9d94e39 100644 --- a/opentok/opentok.py +++ b/opentok/opentok.py @@ -1993,6 +1993,7 @@ def connect_audio_to_websocket( String 'uri': A publicly reachable WebSocket URI to be used for the destination of the audio stream (such as "wss://example.com/ws-endpoint"). List 'streams' Optional: A list of stream IDs for the OpenTok streams you want to include in the WebSocket audio. If you omit this property, all streams in the session will be included. Dictionary 'headers' Optional: An object of key-value pairs of headers to be sent to your WebSocket server with each message, with a maximum length of 512 bytes. + Boolean 'bidirectional' Optional: If true, enables bidirectional audio streaming over the WebSocket connection. """ self.validate_websocket_options(websocket_options) @@ -2044,6 +2045,10 @@ def validate_websocket_options(self, options): if "uri" not in options: raise InvalidWebSocketOptionsError("Provide a WebSocket URI.") + if "bidirectional" in options: + if not isinstance(options["bidirectional"], bool): + raise InvalidWebSocketOptionsError("'bidirectional' must be a boolean if provided.") + def start_captions( self, session_id: str, diff --git a/opentok/version.py b/opentok/version.py index 9dcb0e5..4cddec2 100644 --- a/opentok/version.py +++ b/opentok/version.py @@ -1,3 +1,3 @@ # see: http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers -__version__ = "3.11.1" +__version__ = "3.12.0" diff --git a/opentok/websocket_audio_connection.py b/opentok/websocket_audio_connection.py index 4d4290e..a774152 100644 --- a/opentok/websocket_audio_connection.py +++ b/opentok/websocket_audio_connection.py @@ -7,6 +7,7 @@ class WebSocketAudioConnection: def __init__(self, kwargs): self.id = kwargs.get("id") self.connectionId = kwargs.get("connectionId") + self.bidirectional = kwargs.get("bidirectional") def json(self): """Returns a JSON representation of the WebSocket audio connection information.""" diff --git a/tests/test_audio_connector.py b/tests/test_audio_connector.py index fba29da..5ce8f98 100644 --- a/tests/test_audio_connector.py +++ b/tests/test_audio_connector.py @@ -12,6 +12,38 @@ class OpenTokAudioConnectorLiteTest(unittest.TestCase): + @httpretty.activate + def test_connect_audio_to_websocket_with_bidirectional(self): + httpretty.register_uri( + httpretty.POST, + u(f"https://api.opentok.com/v2/project/{self.api_key}/connect"), + body=self.response_body, + status=200, + content_type=u("application/json"), + ) + + websocket_options = {"uri": "wss://service.com/ws-endpoint", "bidirectional": True} + + websocket_audio_connection = self.opentok.connect_audio_to_websocket( + self.session_id, self.token, websocket_options + ) + + validate_jwt_header(self, httpretty.last_request().headers[u("x-opentok-auth")]) + expect(httpretty.last_request().headers[u("user-agent")]).to(contain(u("OpenTok-Python-SDK/") + __version__)) + expect(httpretty.last_request().headers[u("content-type")]).to(equal(u("application/json"))) + if PY2: + body = json.loads(httpretty.last_request().body) + if PY3: + body = json.loads(httpretty.last_request().body.decode("utf-8")) + + expect(body).to(have_key(u("token"))) + expect(body["websocket"]).to(have_key("bidirectional")) + expect(body["websocket"]["bidirectional"]).to(be_true) + expect(websocket_audio_connection).to(be_a(WebSocketAudioConnection)) + expect(websocket_audio_connection).to(have_property(u("id"), u("b0a5a8c7-dc38-459f-a48d-a7f2008da853"))) + expect(websocket_audio_connection).to(have_property(u("connectionId"), u("e9f8c166-6c67-440d-994a-04fb6dfed007"))) + # The bidirectional property should be present (None if not in response, True if in response) + expect(hasattr(websocket_audio_connection, "bidirectional")).to(be_true) def setUp(self): self.api_key = u("123456") self.api_secret = u("1234567890abcdef1234567890abcdef1234567890")