Skip to content

Commit f738b51

Browse files
maikthomasManik Sachdeva
authored andcommitted
[ECO-2972] [Issue #121] OpenTok.start_archive() doesn't have parameter 'resolution' (#124)
[ECO-2972] [Issue #121] OpenTok.start_archive()
1 parent 68c696a commit f738b51

File tree

7 files changed

+187
-9
lines changed

7 files changed

+187
-9
lines changed

README.rst

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ Creating Sessions
4545
~~~~~~~~~~~~~~~~~
4646

4747
To create an OpenTok Session, use the ``opentok.create_session()`` method. There are three optional
48-
keyword parameters for this method:
48+
keyword parameters for this method:
4949

5050
* ``location`` which can be set to a string containing an IP address.
5151

5252
* ``media_mode`` which is a String (defined by the MediaModes class).
5353
This determines whether the session will use the
5454
`OpenTok Media Router <https://tokbox.com/developer/guides/create-session/#media-mode>`_
55-
or attempt to send streams directly between clients. A routed session is required for some
55+
or attempt to send streams directly between clients. A routed session is required for some
5656
OpenTok features (such as archiving).
5757

5858
* ``archive_mode`` which specifies whether the session will be automatically archived (``always``)
@@ -142,6 +142,20 @@ streams in the session to individual files (instead of a single composed file) b
142142
# Store this archive_id in the database
143143
archive_id = archive.id
144144
145+
Composed archives (output_mode=OutputModes.composed) have an optional ``resolution`` parameter.
146+
If no value is supplied the opentok platform will use the default resolution "640x480".
147+
You can set this to "1280x720" by setting the
148+
``resolution`` parameter of the ``opentok.start_archive()`` method.
149+
150+
Warning: This value cannot be set for Individual output mode, an error will be thrown.
151+
152+
.. code:: python
153+
154+
archive = opentok.start_archive(session_id, name=u'Important Presentation', resolution="1280x720")
155+
156+
# Store this archive_id in the database
157+
archive_id = archive.id
158+
145159
You can stop the recording of a started Archive using the ``opentok.stop_archive(archive_id)``
146160
method. You can also do this using the ``archive.stop()`` method of an ``Archive`` instance.
147161

opentok/archives.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def __init__(self, sdk, values):
102102
self.has_video = values.get('hasVideo')
103103
self.output_mode = OutputModes[values.get('outputMode', 'composed')]
104104
self.url = values.get('url')
105+
self.resolution = values.get('resolution')
105106

106107
def stop(self):
107108
"""

opentok/opentok.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ def archive_url(self, archive_id=None):
311311
url = url + '/' + archive_id
312312
return url
313313

314-
def start_archive(self, session_id, has_audio=True, has_video=True, name=None, output_mode=OutputModes.composed):
314+
def start_archive(self, session_id, has_audio=True, has_video=True, name=None, output_mode=OutputModes.composed, resolution=None):
315315
"""
316316
Starts archiving an OpenTok session.
317317
@@ -338,18 +338,27 @@ def start_archive(self, session_id, has_audio=True, has_video=True, name=None, o
338338
:param OutputModes output_mode: Whether all streams in the archive are recorded
339339
to a single file (OutputModes.composed, the default) or to individual files
340340
(OutputModes.individual).
341+
:param String resolution (Optional): The resolution of the archive, either "640x480" (the default)
342+
or "1280x720". This parameter only applies to composed archives. If you set this
343+
parameter and set the output_mode parameter to OutputModes.individual, the call to the
344+
start_archive() method results in an error.
341345
342346
:rtype: The Archive object, which includes properties defining the archive,
343347
including the archive ID.
344348
"""
345349
if not isinstance(output_mode, OutputModes):
346350
raise OpenTokException(u('Cannot start archive, {0} is not a valid output mode').format(output_mode))
347351

352+
353+
if resolution and output_mode == OutputModes.individual:
354+
raise OpenTokException(u('Invalid parameters: Resolution cannot be supplied for individual output mode.'))
355+
348356
payload = {'name': name,
349357
'sessionId': session_id,
350358
'hasAudio': has_audio,
351359
'hasVideo': has_video,
352-
'outputMode': output_mode.value
360+
'outputMode': output_mode.value,
361+
'resolution': resolution,
353362
}
354363

355364
response = requests.post(self.archive_url(), data=json.dumps(payload), headers=self.archive_headers(), proxies=self.proxies, timeout=self.timeout)
@@ -359,7 +368,14 @@ def start_archive(self, session_id, has_audio=True, has_video=True, name=None, o
359368
elif response.status_code == 403:
360369
raise AuthError()
361370
elif response.status_code == 400:
362-
raise RequestError("Session ID is invalid")
371+
"""
372+
The HTTP response has a 400 status code in the following cases:
373+
You do not pass in a session ID or you pass in an invalid session ID.
374+
No clients are actively connected to the OpenTok session.
375+
You specify an invalid resolution value.
376+
The outputMode property is set to "individual" and you set the resolution property and (which is not supported in individual stream archives).
377+
"""
378+
raise RequestError(response.json().get("message"))
363379
elif response.status_code == 404:
364380
raise NotFoundError("Session not found")
365381
elif response.status_code == 409:

sample/Archiving/archiving.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ def start():
5757
has_audio = 'hasAudio' in request.form.keys()
5858
has_video = 'hasVideo' in request.form.keys()
5959
output_mode = OutputModes[request.form.get('outputMode')]
60+
resolution = request.form.get('resolution')
6061
archive = opentok.start_archive(session.session_id, name="Python Archiving Sample App",
61-
has_audio=has_audio, has_video=has_video, output_mode=output_mode)
62+
has_audio=has_audio, has_video=has_video,
63+
output_mode=output_mode, resolution=resolution)
6264
return archive.json()
6365

6466
@app.route("/stop/<archive_id>")

sample/Archiving/static/js/host.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,32 @@ session.on('archiveStopped', function(event) {
4040
enableForm();
4141
});
4242

43+
function addModeRadioOnClicks() {
44+
var toggleResolution = function(enable) {
45+
return function () {
46+
var resolutionFieldset = $('#resolution-fieldset');
47+
if (enable) {
48+
resolutionFieldset.removeAttr('disabled');
49+
resolutionFieldset.find(':input').removeAttr('disabled')
50+
} else {
51+
resolutionFieldset.attr('disabled', 'disabled')
52+
resolutionFieldset.find(':input').attr('disabled', 'disabled')
53+
}
54+
}
55+
}
56+
57+
$('#composedModeRadio').click(toggleResolution(true));
58+
$('#individualModeRadio').click(toggleResolution(false));
59+
60+
}
61+
4362
$(document).ready(function() {
63+
addModeRadioOnClicks()
64+
4465
$('.start').click(function (event) {
4566
var options = $('.archive-options').serialize();
4667
disableForm();
68+
4769
$.post('/start', options).fail(enableForm);
4870
}).show();
4971
$('.stop').click(function(event){

sample/Archiving/templates/host.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,22 @@ <h3 class="panel-title">Host</h3>
3030
<div class="form-group">
3131
<p class="help-block">Output Mode:</p>
3232
<label class="radio-inline">
33-
<input type="radio" name="outputMode" value="composed" checked> Composed
33+
<input type="radio" id="composedModeRadio" name="outputMode" value="composed" checked> Composed
3434
</label>
3535
<label class="radio-inline">
36-
<input type="radio" name="outputMode" value="individual"> Individual
36+
<input type="radio" id="individualModeRadio" name="outputMode" value="individual"> Individual
3737
</label>
3838
</div>
39+
40+
<fieldset id="resolution-fieldset" class="form-group">
41+
<p class="help-block">Resolution</p>
42+
<label class="radio-inline">
43+
<input type="radio" name="resolution" value="640x480" checked> 640x480
44+
</label>
45+
<label class="radio-inline">
46+
<input type="radio" name="resolution" value="1280x720"> 1280x720
47+
</label>
48+
</fieldset>
3949
</fieldset>
4050
</form>
4151
<button class="btn btn-danger start">Start archiving</button>

tests/test_archive_api.py

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import pytz
1010
from .validate_jwt import validate_jwt_header
1111

12-
from opentok import OpenTok, Archive, ArchiveList, OutputModes, __version__
12+
from opentok import OpenTok, Archive, ArchiveList, OutputModes, OpenTokException, __version__
1313

1414
class OpenTokArchiveApiTest(unittest.TestCase):
1515
def setUp(self):
@@ -118,6 +118,119 @@ def test_start_archive_with_name(self):
118118
expect(archive).to(have_property(u('duration'), equal(0)))
119119
expect(archive).to(have_property(u('url'), equal(None)))
120120

121+
@httpretty.activate
122+
def test_start_archive_with_640x480_resolution(self):
123+
httpretty.register_uri(httpretty.POST, u('https://api.opentok.com/v2/project/{0}/archive').format(self.api_key),
124+
body=textwrap.dedent(u("""\
125+
{
126+
"createdAt" : 1395183243556,
127+
"duration" : 0,
128+
"id" : "30b3ebf1-ba36-4f5b-8def-6f70d9986fe9",
129+
"name" : "ARCHIVE NAME",
130+
"partnerId" : 123456,
131+
"reason" : "",
132+
"sessionId" : "SESSIONID",
133+
"size" : 0,
134+
"status" : "started",
135+
"hasAudio": true,
136+
"hasVideo": true,
137+
"outputMode": "composed",
138+
"url" : null,
139+
"resolution": "640x480"
140+
}""")),
141+
status=200,
142+
content_type=u('application/json'))
143+
144+
archive = self.opentok.start_archive(self.session_id, resolution="640x480")
145+
146+
validate_jwt_header(self, httpretty.last_request().headers[u('x-opentok-auth')])
147+
expect(httpretty.last_request().headers[u('user-agent')]).to(contain(u('OpenTok-Python-SDK/')+__version__))
148+
expect(httpretty.last_request().headers[u('content-type')]).to(equal(u('application/json')))
149+
# non-deterministic json encoding. have to decode to test it properly
150+
if PY2:
151+
body = json.loads(httpretty.last_request().body)
152+
if PY3:
153+
body = json.loads(httpretty.last_request().body.decode('utf-8'))
154+
expect(body).to(have_key(u('sessionId'), u('SESSIONID')))
155+
expect(body).to(have_key(u('resolution'), u('640x480')))
156+
expect(archive).to(be_an(Archive))
157+
expect(archive).to(have_property(u('id'), u('30b3ebf1-ba36-4f5b-8def-6f70d9986fe9')))
158+
expect(archive).to(have_property(u('resolution'), "640x480"))
159+
expect(archive).to(have_property(u('status'), u('started')))
160+
expect(archive).to(have_property(u('session_id'), u('SESSIONID')))
161+
expect(archive).to(have_property(u('partner_id'), 123456))
162+
if PY2:
163+
created_at = datetime.datetime.fromtimestamp(1395183243, pytz.UTC)
164+
if PY3:
165+
created_at = datetime.datetime.fromtimestamp(1395183243, datetime.timezone.utc)
166+
expect(archive).to(have_property(u('created_at'), equal(created_at)))
167+
expect(archive).to(have_property(u('size'), equal(0)))
168+
expect(archive).to(have_property(u('duration'), equal(0)))
169+
expect(archive).to(have_property(u('url'), equal(None)))
170+
171+
@httpretty.activate
172+
def test_start_archive_with_1280x720_resolution(self):
173+
httpretty.register_uri(httpretty.POST, u('https://api.opentok.com/v2/project/{0}/archive').format(self.api_key),
174+
body=textwrap.dedent(u("""\
175+
{
176+
"createdAt" : 1395183243556,
177+
"duration" : 0,
178+
"id" : "30b3ebf1-ba36-4f5b-8def-6f70d9986fe9",
179+
"name" : "ARCHIVE NAME",
180+
"partnerId" : 123456,
181+
"reason" : "",
182+
"sessionId" : "SESSIONID",
183+
"size" : 0,
184+
"status" : "started",
185+
"hasAudio": true,
186+
"hasVideo": true,
187+
"outputMode": "composed",
188+
"url" : null,
189+
"resolution": "1280x720"
190+
}""")),
191+
status=200,
192+
content_type=u('application/json'))
193+
194+
archive = self.opentok.start_archive(self.session_id, resolution="1280x720")
195+
196+
validate_jwt_header(self, httpretty.last_request().headers[u('x-opentok-auth')])
197+
expect(httpretty.last_request().headers[u('user-agent')]).to(contain(u('OpenTok-Python-SDK/')+__version__))
198+
expect(httpretty.last_request().headers[u('content-type')]).to(equal(u('application/json')))
199+
# non-deterministic json encoding. have to decode to test it properly
200+
if PY2:
201+
body = json.loads(httpretty.last_request().body)
202+
if PY3:
203+
body = json.loads(httpretty.last_request().body.decode('utf-8'))
204+
expect(body).to(have_key(u('sessionId'), u('SESSIONID')))
205+
expect(body).to(have_key(u('resolution'), u('1280x720')))
206+
expect(archive).to(be_an(Archive))
207+
expect(archive).to(have_property(u('id'), u('30b3ebf1-ba36-4f5b-8def-6f70d9986fe9')))
208+
expect(archive).to(have_property(u('resolution'), "1280x720"))
209+
expect(archive).to(have_property(u('status'), u('started')))
210+
expect(archive).to(have_property(u('session_id'), u('SESSIONID')))
211+
expect(archive).to(have_property(u('partner_id'), 123456))
212+
if PY2:
213+
created_at = datetime.datetime.fromtimestamp(1395183243, pytz.UTC)
214+
if PY3:
215+
created_at = datetime.datetime.fromtimestamp(1395183243, datetime.timezone.utc)
216+
expect(archive).to(have_property(u('created_at'), equal(created_at)))
217+
expect(archive).to(have_property(u('size'), equal(0)))
218+
expect(archive).to(have_property(u('duration'), equal(0)))
219+
expect(archive).to(have_property(u('url'), equal(None)))
220+
221+
def test_start_archive_individual_and_resolution_throws_error(self):
222+
self.assertRaises(OpenTokException,
223+
self.opentok.start_archive,
224+
session_id=self.session_id,
225+
output_mode=OutputModes.individual,
226+
resolution="640x480")
227+
228+
self.assertRaises(OpenTokException,
229+
self.opentok.start_archive,
230+
session_id=self.session_id,
231+
output_mode=OutputModes.individual,
232+
resolution="1280x720")
233+
121234
@httpretty.activate
122235
def test_start_voice_archive(self):
123236
httpretty.register_uri(httpretty.POST, u('https://api.opentok.com/v2/project/{0}/archive').format(self.api_key),

0 commit comments

Comments
 (0)