Skip to content

Commit 307948f

Browse files
Merge pull request #150 from opentok/dev-upgrade
Moves the `dev` branch to match `master`
2 parents ec40229 + ffc6714 commit 307948f

File tree

8 files changed

+646
-4
lines changed

8 files changed

+646
-4
lines changed

README.rst

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ http://www.pip-installer.org/en/latest/
1919
Add the ``opentok`` package as a dependency in your project. The most common way is to add it to your
2020
``requirements.txt`` file::
2121

22-
opentok>=2.9.0
22+
opentok>=2.10.0
2323

2424
Next, install the dependencies::
2525

@@ -362,6 +362,108 @@ You can connect your SIP platform to an OpenTok session, the audio from your end
362362
For more information, including technical details and security considerations, see the
363363
`OpenTok SIP interconnect <https://tokbox.com/developer/guides/sip/>`_ developer guide.
364364

365+
Working with Broadcasts
366+
~~~~~~~~~~~~~~~~~~~~~~~
367+
368+
OpenTok broadcast lets you share live OpenTok sessions with many viewers.
369+
370+
You can use the ``opentok.start_broadcast()`` method to start a live streaming for an OpenTok session. This broadcasts the session to an HLS (HTTP live streaming) or to RTMP streams.
371+
372+
To successfully start broadcasting a session, at least one client must be connected to the session.
373+
374+
The live streaming broadcast can target one HLS endpoint and up to five RTMP servers simulteneously for a session. You can only start live streaming for sessions that use the OpenTok Media Router; you cannot use live streaming with sessions that have the media mode set to relayed.
375+
376+
.. code:: python
377+
378+
session_id = 'SESSIONID'
379+
options = {
380+
'layout': {
381+
'type': 'custom',
382+
'stylesheet': 'the layout stylesheet (only used with type == custom)'
383+
},
384+
'maxDuration': 5400,
385+
'outputs': {
386+
'hls': {},
387+
'rtmp': [{
388+
'id': 'foo',
389+
'serverUrl': 'rtmp://myfooserver/myfooapp',
390+
'streamName': 'myfoostream'
391+
}, {
392+
'id': 'bar',
393+
'serverUrl': 'rtmp://mybarserver/mybarapp',
394+
'streamName': 'mybarstream'
395+
}]
396+
},
397+
'resolution': '640x480'
398+
}
399+
400+
broadcast = opentok.start_broadcast(session_id, options)
401+
402+
You can stop a started Broadcast using the ``opentok.stop_broadcast(broadcast_id)`` method.
403+
404+
.. code:: python
405+
406+
# getting the ID from a broadcast object
407+
broadcast_id = broadcast.id
408+
409+
# stop a broadcast
410+
broadcast = opentok.stop_broadcast(broadcast_id)
411+
412+
You can get details on a broadcast that is in-progress using the method ``opentok.get_broadcast(broadcast_id)``.
413+
414+
.. code:: python
415+
416+
broadcast_id = '1748b7070a81464c9759c46ad10d3734'
417+
418+
# get broadcast details
419+
broadcast = opentok.get_broadcast(broadcast_id)
420+
421+
print broadcast.json()
422+
423+
# print result
424+
# {
425+
# "createdAt": 1437676551000,
426+
# "id": "1748b707-0a81-464c-9759-c46ad10d3734",
427+
# "projectId": 100,
428+
# "resolution": "640x480",
429+
# "sessionId": "2_MX4xMDBfjE0Mzc2NzY1NDgwMTJ-TjMzfn4",
430+
# "status": "started",
431+
# "updatedAt": 1437676551000,
432+
# "broadcastUrls": {
433+
# "hls": "http://server/fakepath/playlist.m3u8",
434+
# "rtmp": {
435+
# "bar": {
436+
# "serverUrl": "rtmp://mybarserver/mybarapp",
437+
# "status": "live",
438+
# "streamName": "mybarstream"
439+
# },
440+
# "foo": {
441+
# "serverUrl": "rtmp://myfooserver/myfooapp",
442+
# "status": "live",
443+
# "streamName": "myfoostream"
444+
# }
445+
# }
446+
# }
447+
# }
448+
449+
You can dynamically change the layout type of a live streaming broadcast.
450+
451+
.. code:: python
452+
453+
# Valid values to 'layout_type' are: 'custom', 'horizontalPresentation',
454+
# 'pip' and 'verticalPresentation'
455+
opentok.set_broadcast_layout('BROADCASTID', 'horizontalPresentation')
456+
457+
# if you specify a 'custom' layout type, set the stylesheet parameter:
458+
opentok.set_broadcast_layout(
459+
'BROADCASTID',
460+
'custom',
461+
'stream.instructor {position: absolute; width: 100%; height:50%;}'
462+
)
463+
464+
For more information about OpenTok live streaming broadcasts, see the
465+
`Broadcast developer guide <https://tokbox.com/developer/guides/broadcast/>`_.
466+
365467
Samples
366468
-------
367469

opentok/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
AuthError,
77
ForceDisconnectError,
88
ArchiveError,
9-
SetStreamClassError
9+
SetStreamClassError,
10+
BroadcastError
1011
)
1112
from .version import __version__
1213
from .stream import Stream
1314
from .streamlist import StreamList
1415
from .sip_call import SipCall
16+
from .broadcast import Broadcast

opentok/broadcast.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import json
2+
3+
class Broadcast(object):
4+
"""
5+
Represents a live streaming broadcast
6+
"""
7+
8+
def __init__(self, kwargs):
9+
self.id = kwargs.get('id')
10+
self.sessionId = kwargs.get('sessionId')
11+
self.projectId = kwargs.get('projectId')
12+
self.createdAt = kwargs.get('createdAt')
13+
self.updatedAt = kwargs.get('updatedAt')
14+
self.resolution = kwargs.get('resolution')
15+
self.status = kwargs.get('status')
16+
self.broadcastUrls = kwargs.get('broadcastUrls')
17+
18+
def json(self):
19+
"""
20+
Returns a JSON representation of the broadcast
21+
"""
22+
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)

opentok/endpoints.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,15 @@ def set_stream_class_lists_url(self, session_id):
5555
""" this method returns the url to set the stream class list """
5656
url = self.api_url + '/v2/project/' + self.api_key + '/session/' + session_id + '/stream'
5757
return url
58+
59+
def broadcast_url(self, broadcast_id=None, stop=False, layout=False):
60+
""" this method returns urls for working with broadcast """
61+
url = self.api_url + '/v2/project/' + self.api_key + '/broadcast'
62+
63+
if broadcast_id:
64+
url = url + '/' + broadcast_id
65+
if stop:
66+
url = url + '/stop'
67+
if layout:
68+
url = url + '/layout'
69+
return url

opentok/exceptions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,12 @@ class SetStreamClassError(OpenTokException):
6363
It may also indicate that invalid layout options have been passed
6464
"""
6565
pass
66+
67+
class BroadcastError(OpenTokException):
68+
"""
69+
Indicates that data in your request data is invalid JSON. It may also indicate
70+
that you passed in invalid layout options. Or you have exceeded the limit of five
71+
simultaneous RTMP streams for an OpenTok session. Or you specified and invalid resolution.
72+
Or The broadcast has already started for the session
73+
"""
74+
pass

opentok/opentok.py

Lines changed: 173 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .stream import Stream
2626
from .streamlist import StreamList
2727
from .sip_call import SipCall
28+
from .broadcast import Broadcast
2829
from .exceptions import (
2930
OpenTokException,
3031
RequestError,
@@ -35,7 +36,8 @@
3536
GetStreamError,
3637
ForceDisconnectError,
3738
SipDialError,
38-
SetStreamClassError
39+
SetStreamClassError,
40+
BroadcastError
3941
)
4042

4143
class Roles(Enum):
@@ -761,6 +763,176 @@ class names (Strings) to apply to the stream. For example:
761763
else:
762764
raise RequestError('OpenTok server error.', response.status_code)
763765

766+
def start_broadcast(self, session_id, options):
767+
"""
768+
Use this method to start a live streaming for an OpenTok session. This broadcasts the
769+
session to an HLS (HTTP live streaming) or to RTMP streams. To successfully start
770+
broadcasting a session, at least one client must be connected to the session. You can only
771+
start live streaming for sessions that use the OpenTok Media Router (with the media mode set
772+
to routed); you cannot use live streaming with sessions that have the media mode set to
773+
relayed
774+
775+
:param String session_id: The session ID of the OpenTok session you want to broadcast
776+
777+
:param Dictionary options, with the following properties:
778+
779+
Dictionary 'layout' optional: Specify this to assign the initial layout type for the
780+
broadcast. Valid values for the layout property are "bestFit", "custom",
781+
"horizontalPresentation", "pip" and "verticalPresentation". If you specify a "custom"
782+
layout type, set the stylesheet property of the layout object to the stylesheet.
783+
If you do not specify an initial layout type, the broadcast stream uses the Best Fit
784+
layout type
785+
786+
Integer 'maxDuration' optional: The maximum duration for the broadcast, in seconds.
787+
The broadcast will automatically stop when the maximum duration is reached. You can
788+
set the maximum duration to a value from 60 (60 seconds) to 36000 (10 hours). The
789+
default maximum duration is 2 hours (7200 seconds)
790+
791+
Dictionary 'outputs': This object defines the types of broadcast streams you want to
792+
start (both HLS and RTMP). You can include HLS, RTMP, or both as broadcast streams.
793+
If you include RTMP streaming, you can specify up to five target RTMP streams. For
794+
each RTMP stream, specify 'serverUrl' (the RTMP server URL), 'streamName' (the stream
795+
name, such as the YouTube Live stream name or the Facebook stream key), and
796+
(optionally) 'id' (a unique ID for the stream)
797+
798+
String 'resolution' optional: The resolution of the broadcast, either "640x480"
799+
(SD, the default) or "1280x720" (HD)
800+
801+
:rtype A Broadcast object, which contains information of the broadcast: id, sessionId
802+
projectId, createdAt, updatedAt, resolution, status and broadcastUrls
803+
"""
804+
payload = {
805+
'sessionId': session_id
806+
}
807+
808+
payload.update(options)
809+
810+
endpoint = self.endpoints.broadcast_url()
811+
response = requests.post(
812+
endpoint,
813+
data=json.dumps(payload),
814+
headers=self.json_headers(),
815+
proxies=self.proxies,
816+
timeout=self.timeout
817+
)
818+
819+
if response.status_code == 200:
820+
return Broadcast(response.json())
821+
elif response.status_code == 400:
822+
raise BroadcastError(
823+
'Invalid request. This response may indicate that data in your request data is '
824+
'invalid JSON. It may also indicate that you passed in invalid layout options. '
825+
'Or you have exceeded the limit of five simultaneous RTMP streams for an OpenTok '
826+
'session. Or you specified and invalid resolution.')
827+
elif response.status_code == 403:
828+
raise AuthError('Authentication error.')
829+
elif response.status_code == 409:
830+
raise BroadcastError('The broadcast has already started for the session.')
831+
else:
832+
raise RequestError('OpenTok server error.', response.status_code)
833+
834+
def stop_broadcast(self, broadcast_id):
835+
"""
836+
Use this method to stop a live broadcast of an OpenTok session
837+
838+
:param String broadcast_id: The ID of the broadcast you want to stop
839+
840+
:rtype A Broadcast object, which contains information of the broadcast: id, sessionId
841+
projectId, createdAt, updatedAt and resolution
842+
"""
843+
endpoint = self.endpoints.broadcast_url(broadcast_id, stop=True)
844+
response = requests.post(
845+
endpoint,
846+
headers=self.json_headers(),
847+
proxies=self.proxies,
848+
timeout=self.timeout
849+
)
850+
851+
if response.status_code == 200:
852+
return Broadcast(response.json())
853+
elif response.status_code == 400:
854+
raise BroadcastError(
855+
'Invalid request. This response may indicate that data in your request '
856+
'data is invalid JSON.')
857+
elif response.status_code == 403:
858+
raise AuthError('Authentication error.')
859+
elif response.status_code == 409:
860+
raise BroadcastError(
861+
'The broadcast (with the specified ID) was not found or it has already '
862+
'stopped.')
863+
else:
864+
raise RequestError('OpenTok server error.', response.status_code)
865+
866+
def get_broadcast(self, broadcast_id):
867+
"""
868+
Use this method to get details on a broadcast that is in-progress.
869+
870+
:param String broadcast_id: The ID of the broadcast you want to stop
871+
872+
:rtype A Broadcast object, which contains information of the broadcast: id, sessionId
873+
projectId, createdAt, updatedAt, resolution, broadcastUrls and status
874+
"""
875+
endpoint = self.endpoints.broadcast_url(broadcast_id)
876+
response = requests.get(
877+
endpoint,
878+
headers=self.json_headers(),
879+
proxies=self.proxies,
880+
timeout=self.timeout
881+
)
882+
883+
if response.status_code == 200:
884+
return Broadcast(response.json())
885+
elif response.status_code == 400:
886+
raise BroadcastError(
887+
'Invalid request. This response may indicate that data in your request '
888+
'data is invalid JSON.')
889+
elif response.status_code == 403:
890+
raise AuthError('Authentication error.')
891+
elif response.status_code == 409:
892+
raise BroadcastError('No matching broadcast found (with the specified ID).')
893+
else:
894+
raise RequestError('OpenTok server error.', response.status_code)
895+
896+
def set_broadcast_layout(self, broadcast_id, layout_type, stylesheet=None):
897+
"""
898+
Use this method to change the layout type of a live streaming broadcast
899+
900+
:param String broadcast_id: The ID of the broadcast that will be updated
901+
902+
:param String layout_type: The layout type for the broadcast. Valid values are:
903+
'bestFit', 'custom', 'horizontalPresentation', 'pip' and 'verticalPresentation'
904+
905+
:param String stylesheet optional: CSS used to style the custom layout.
906+
Specify this only if you set the type property to 'custom'
907+
"""
908+
payload = {
909+
'type': layout_type,
910+
}
911+
912+
if layout_type == 'custom':
913+
if stylesheet is not None:
914+
payload['stylesheet'] = stylesheet
915+
916+
endpoint = self.endpoints.broadcast_url(broadcast_id, layout=True)
917+
response = requests.put(
918+
endpoint,
919+
data=json.dumps(payload),
920+
headers=self.json_headers(),
921+
proxies=self.proxies,
922+
timeout=self.timeout
923+
)
924+
925+
if response.status_code == 200:
926+
pass
927+
elif response.status_code == 400:
928+
raise BroadcastError(
929+
'Invalid request. This response may indicate that data in your request data is '
930+
'invalid JSON. It may also indicate that you passed in invalid layout options.')
931+
elif response.status_code == 403:
932+
raise AuthError('Authentication error.')
933+
else:
934+
raise RequestError('OpenTok server error.', response.status_code)
935+
764936
def _sign_string(self, string, secret):
765937
return hmac.new(secret.encode('utf-8'), string.encode('utf-8'), hashlib.sha1).hexdigest()
766938

opentok/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# see: http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers
2-
__version__ = '2.9.0'
2+
__version__ = '2.10.0'

0 commit comments

Comments
 (0)