Skip to content

Commit a3df828

Browse files
normanarguetaManik Sachdeva
authored andcommitted
Adding set_stream_class_list() method (#141)
* Add set_stream_class_lists API
1 parent 2d59858 commit a3df828

File tree

6 files changed

+138
-6
lines changed

6 files changed

+138
-6
lines changed

README.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,27 @@ The method returns a StreamList object that contains a list of all the streams
294294
print stream.name #stream name
295295
print stream.layoutClassList #['full']
296296
297+
You can change the layout classes for streams in a session by calling the `set_stream_class_lists(session_id, stream_list)` method of the `OpenTok` class.
298+
299+
The layout classes define how the stream is displayed in the layout of a composed OpenTok archive.
300+
301+
.. code:: python
302+
303+
# This list contains the information of the streams that will be updated. Each element
304+
# in the list is a dictionary with two properties: 'id' and 'layoutClassList'. The 'id'
305+
# property is the stream ID (a String), and the 'layoutClassList' is an array of class
306+
# names (Strings) to apply to the stream.
307+
payload = [
308+
{'id': '7b09ec3c-26f9-43d7-8197-f608f13d4fb6', 'layoutClassList': ['focus']},
309+
{'id': '567bc941-6ea0-4c69-97fc-70a740b68976', 'layoutClassList': ['top']},
310+
{'id': '307dc941-0450-4c09-975c-705740d08970', 'layoutClassList': ['bottom']}
311+
]
312+
313+
opentok.set_stream_class_lists('SESSIONID', payload)
314+
315+
For more information see
316+
`Changing the composed archive layout classes for an OpenTok stream <https://tokbox.com/developer/rest/#change-stream-layout-classes-composed>`_.
317+
297318
Force Disconnect
298319
~~~~~~~~~~~~~~~~~~~~~
299320

opentok/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
from .opentok import OpenTok, Roles, MediaModes, ArchiveModes
22
from .session import Session
33
from .archives import Archive, ArchiveList, OutputModes
4-
from .exceptions import OpenTokException, AuthError, ForceDisconnectError, ArchiveError
4+
from .exceptions import (
5+
OpenTokException,
6+
AuthError,
7+
ForceDisconnectError,
8+
ArchiveError,
9+
SetStreamClassError
10+
)
511
from .version import __version__
612
from .stream import Stream
713
from .streamlist import StreamList

opentok/endpoints.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,8 @@ def dial_url(self):
5050
""" this method returns the url to initialize a SIP call """
5151
url = self.api_url + '/v2/project/' + self.api_key + '/dial'
5252
return url
53+
54+
def set_stream_class_lists_url(self, session_id):
55+
""" this method returns the url to set the stream class list """
56+
url = self.api_url + '/v2/project/' + self.api_key + '/session/' + session_id + '/stream'
57+
return url

opentok/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,10 @@ class SipDialError(OpenTokException):
5656
that does not use the OpenTok Media Router.
5757
"""
5858
pass
59+
60+
class SetStreamClassError(OpenTokException):
61+
"""
62+
Indicates that there is invalid data in the JSON request.
63+
It may also indicate that invalid layout options have been passed
64+
"""
65+
pass

opentok/opentok.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
SignalingError,
3535
GetStreamError,
3636
ForceDisconnectError,
37-
SipDialError
37+
SipDialError,
38+
SetStreamClassError
3839
)
3940

4041
class Roles(Enum):
@@ -530,7 +531,7 @@ def signal(self, session_id, payload, connection_id=None):
530531
def get_stream(self, session_id, stream_id):
531532
"""
532533
Returns an Stream object that contains information of an OpenTok stream:
533-
534+
534535
-id: The stream ID
535536
-videoType: "camera" or "screen"
536537
-name: The stream name (if one was set when the client published the stream)
@@ -638,7 +639,6 @@ def set_archive_layout(self, archive_id, layout_type, stylesheet=None):
638639
else:
639640
raise RequestError('OpenTok server error.', response.status_code)
640641

641-
642642
def dial(self, session_id, token, sip_uri, options=[]):
643643
"""
644644
Use this method to connect a SIP platform to an OpenTok session. The audio from the end
@@ -720,6 +720,47 @@ def dial(self, session_id, token, sip_uri, options=[]):
720720
else:
721721
raise RequestError('OpenTok server error.', response.status_code)
722722

723+
def set_stream_class_lists(self, session_id, payload):
724+
"""
725+
Use this method to change layout classes for OpenTok streams. The layout classes
726+
define how the streams are displayed in the layout of a composed OpenTok archive
727+
728+
:param String session_id: The ID of the session of the streams that will be updated
729+
730+
:param List payload: A list defining the class lists to apply to the streams.
731+
Each element in the list is a dictionary with two properties: 'id' and 'layoutClassList'.
732+
The 'id' property is the stream ID (a String), and the 'layoutClassList' is an array of
733+
class names (Strings) to apply to the stream. For example:
734+
735+
payload = [
736+
{'id': '7b09ec3c-26f9-43d7-8197-f608f13d4fb6', 'layoutClassList': ['focus']},
737+
{'id': '567bc941-6ea0-4c69-97fc-70a740b68976', 'layoutClassList': ['top']},
738+
{'id': '307dc941-0450-4c09-975c-705740d08970', 'layoutClassList': ['bottom']}
739+
]
740+
"""
741+
items_payload = {'items': payload}
742+
743+
endpoint = self.endpoints.set_stream_class_lists_url(session_id)
744+
response = requests.put(
745+
endpoint,
746+
data=json.dumps(items_payload),
747+
headers=self.json_headers(),
748+
proxies=self.proxies,
749+
timeout=self.timeout
750+
)
751+
752+
if response.status_code == 200:
753+
pass
754+
elif response.status_code == 400:
755+
raise SetStreamClassError(
756+
'Invalid request. This response may indicate that data in your request data '
757+
'is invalid JSON. It may also indicate that you passed in invalid layout options.'
758+
)
759+
elif response.status_code == 403:
760+
raise AuthError('Authentication error.')
761+
else:
762+
raise RequestError('OpenTok server error.', response.status_code)
763+
723764
def _sign_string(self, string, secret):
724765
return hmac.new(secret.encode('utf-8'), string.encode('utf-8'), hashlib.sha1).hexdigest()
725766

tests/test_get_stream.py renamed to tests/test_stream.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import unittest
22
from six import text_type, u, b, PY2, PY3
3-
from opentok import OpenTok, Stream, StreamList, __version__
3+
from opentok import OpenTok, Stream, StreamList, __version__, SetStreamClassError
44
import httpretty
55
import json
66
import textwrap
77
from expects import *
88

99
from .validate_jwt import validate_jwt_header
1010

11-
class OpenTokGetStreamTest(unittest.TestCase):
11+
class OpenTokStreamTest(unittest.TestCase):
1212
def setUp(self):
1313
self.api_key = u('123456')
1414
self.api_secret = u('1234567890abcdef1234567890abcdef1234567890')
@@ -118,3 +118,55 @@ def test_get_stream_list(self):
118118
expect(stream_list).to(be_an(StreamList))
119119
expect(stream_list).to(have_property(u('count'), 2))
120120
expect(list(stream_list.items)).to(have_length(2))
121+
122+
@httpretty.activate
123+
def test_set_stream_class_lists(self):
124+
""" Test set stream class functionality """
125+
payload = [
126+
{'id': '7b09ec3c-26f9-43d7-8197-f608f13d4fb6', 'layoutClassList': ['focus']},
127+
{'id': '567bc941-6ea0-4c69-97fc-70a740b68976', 'layoutClassList': ['top']},
128+
{'id': '307dc941-0450-4c09-975c-705740d08970', 'layoutClassList': ['bottom']}
129+
]
130+
131+
httpretty.register_uri(
132+
httpretty.PUT,
133+
u('https://api.opentok.com/v2/project/{0}/session/{1}/stream').format(
134+
self.api_key,
135+
self.session_id
136+
),
137+
status=200,
138+
content_type=u('application/json')
139+
)
140+
141+
self.opentok.set_stream_class_lists(self.session_id, payload)
142+
143+
validate_jwt_header(self, httpretty.last_request().headers[u('x-opentok-auth')])
144+
expect(httpretty.last_request().headers[u('user-agent')]).to(contain(
145+
u('OpenTok-Python-SDK/')+__version__))
146+
expect(httpretty.last_request().headers[u('content-type')]).to(equal(u('application/json')))
147+
148+
@httpretty.activate
149+
def test_set_stream_class_lists_throws_exception(self):
150+
""" Test invalid request in set stream class list """
151+
152+
#invalid payload
153+
payload = [
154+
{'id': '7b09ec3c-26f9-43d7-8197-f608f13d4fb6'}
155+
]
156+
157+
httpretty.register_uri(
158+
httpretty.PUT,
159+
u('https://api.opentok.com/v2/project/{0}/session/{1}/stream').format(
160+
self.api_key,
161+
self.session_id
162+
),
163+
status=400,
164+
content_type=u('application/json')
165+
)
166+
167+
self.assertRaises(
168+
SetStreamClassError,
169+
self.opentok.set_stream_class_lists,
170+
self.session_id,
171+
payload
172+
)

0 commit comments

Comments
 (0)