Skip to content

Commit bf19f14

Browse files
committed
Merge pull request #62 from jcague/feature/archiving_outputmode
Added support to outputMode
2 parents c3cc395 + 3b3b279 commit bf19f14

File tree

8 files changed

+204
-21
lines changed

8 files changed

+204
-21
lines changed

opentok/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from .opentok import OpenTok, Roles, MediaModes
22
from .session import Session
3-
from .archives import Archive, ArchiveList
3+
from .archives import Archive, ArchiveList, OutputModes
44
from .exceptions import OpenTokException
55
from .version import __version__

opentok/archives.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from six import iteritems, PY2, PY3, u
33
import json
44
import pytz
5+
from enum import Enum
56
if PY3:
67
from datetime import timezone
78

@@ -10,6 +11,14 @@
1011

1112
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) or isinstance(obj, date) else None
1213

14+
class OutputModes(Enum):
15+
"""List of valid settings for the outputMode parameter of the OpenTok.start_archive() method."""
16+
composed = u('composed')
17+
"""The archive will produce a single MP4 file composed of all streams."""
18+
individual = u('individual')
19+
"""The archive will generate a ZIP container with multiple individual WEBM files and a JSON metadata
20+
file for video synchronization."""
21+
1322
class Archive(object):
1423
"""Represents an archive of an OpenTok session.
1524
@@ -27,6 +36,12 @@ class Archive(object):
2736
Boolean value set to true when the archive contains a video track,
2837
and set to false otherwise.
2938
39+
:ivar outputMode:
40+
The output mode to be generated for this archive:
41+
The default value is composed (a single MP4 file composed of all streams).
42+
Value individual will generate a ZIP container with multiple individual WEBM files
43+
and a JSON metadata file for video synchronization.
44+
3045
:ivar id:
3146
The archive ID.
3247
@@ -80,8 +95,9 @@ def __init__(self, sdk, values):
8095
self.created_at = datetime.fromtimestamp(values.get('createdAt') // 1000, timezone.utc)
8196
self.size = values.get('size')
8297
self.duration = values.get('duration')
83-
self.hasAudio = values.get('hasAudio')
84-
self.hasVideo = values.get('hasVideo')
98+
self.has_audio = values.get('hasAudio')
99+
self.has_video = values.get('hasVideo')
100+
self.output_mode = OutputModes[values.get('outputMode', 'composed')]
85101
self.url = values.get('url')
86102

87103
def stop(self):

opentok/opentok.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from .version import __version__
1919
from .session import Session
20-
from .archives import Archive, ArchiveList
20+
from .archives import Archive, ArchiveList, OutputModes
2121
from .exceptions import OpenTokException, RequestError, AuthError, NotFoundError, ArchiveError
2222

2323
class Roles(Enum):
@@ -264,7 +264,7 @@ def archive_url(self, archive_id=None):
264264
url = url + '/' + archive_id
265265
return url
266266

267-
def start_archive(self, session_id, hasAudio=True, hasVideo=True, name=None):
267+
def start_archive(self, session_id, has_audio=True, has_video=True, name=None, output_mode=OutputModes.composed):
268268
"""
269269
Starts archiving an OpenTok 2.0 session.
270270
@@ -285,14 +285,22 @@ def start_archive(self, session_id, hasAudio=True, hasVideo=True, name=None):
285285
an error.
286286
:param Boolean hasVideo: if set to True, a video track will be inserted to the archive.
287287
hasVideo is an optional parameter that is set to True by default.
288+
:param OutputModes outputMode: if set to composed, a single MP4 file composed of all streams
289+
will be generated. If you set it to individual it will create a ZIP container with multiple
290+
individual WEBM files and a JSON metadata file for video synchronization.
291+
outputMode is an optional parameter that is set to composed by default.
288292
289293
:rtype: The Archive object, which includes properties defining the archive,
290294
including the archive ID.
291295
"""
296+
if not isinstance(output_mode, OutputModes):
297+
raise OpenTokException(u('Cannot start archive, {0} is not a valid output mode').format(output_mode))
298+
292299
payload = {'name': name,
293300
'sessionId': session_id,
294-
'hasAudio': hasAudio,
295-
'hasVideo': hasVideo
301+
'hasAudio': has_audio,
302+
'hasVideo': has_video,
303+
'outputMode': output_mode.value
296304
}
297305

298306
response = requests.post(self.archive_url(), data=json.dumps(payload), headers=self.archive_headers())

sample/Archiving/archiving.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from flask import Flask, render_template, request, redirect, url_for
2-
from opentok import OpenTok, MediaModes
2+
from opentok import OpenTok, MediaModes, OutputModes
33
from email.utils import formatdate
44
import os, time
55

@@ -52,9 +52,13 @@ def download(archive_id):
5252
archive = opentok.get_archive(archive_id)
5353
return redirect(archive.url)
5454

55-
@app.route("/start")
55+
@app.route("/start", methods=['POST'])
5656
def start():
57-
archive = opentok.start_archive(session.session_id, name="Python Archiving Sample App")
57+
has_audio = 'hasAudio' in request.form.keys()
58+
has_video = 'hasVideo' in request.form.keys()
59+
output_mode = OutputModes[request.form.get('outputMode')]
60+
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)
5862
return archive.json()
5963

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

sample/Archiving/static/js/host.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,33 @@ session.on('archiveStarted', function(event) {
1818
console.log("ARCHIVE STARTED");
1919
$(".start").hide();
2020
$(".stop").show();
21+
disableForm();
2122
});
2223

2324
session.on('archiveStopped', function(event) {
2425
archiveID = null;
2526
console.log("ARCHIVE STOPPED");
2627
$(".start").show();
2728
$(".stop").hide();
29+
enableForm();
2830
});
2931

3032
$(document).ready(function() {
31-
$(".start").click(function(event){
32-
$.get("start");
33+
$(".start").click(function (event) {
34+
var options = $(".archive-options").serialize();
35+
disableForm();
36+
$.post("/start", options).fail(enableForm);
3337
}).show();
3438
$(".stop").click(function(event){
3539
$.get("stop/" + archiveID);
3640
}).hide();
3741
});
42+
43+
44+
function disableForm() {
45+
$(".archive-options-fields").attr('disabled', 'disabled');
46+
}
47+
48+
function enableForm() {
49+
$(".archive-options-fields").removeAttr('disabled');
50+
}

sample/Archiving/templates/host.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,29 @@ <h3 class="panel-title">Host</h3>
1515
<div id="subscribers"><div id="publisher"></div></div>
1616
</div>
1717
<div class="panel-footer">
18+
<form class="archive-options">
19+
<fieldset class="archive-options-fields">
20+
<div class="form-group">
21+
<p class="help-block">Archive Options:</p>
22+
<label class="checkbox-inline">
23+
<input type="checkbox" name="hasAudio" checked> Audio
24+
</label>
25+
<label class="checkbox-inline">
26+
<input type="checkbox" name="hasVideo" checked> Video
27+
</label>
28+
</div>
29+
30+
<div class="form-group">
31+
<p class="help-block">Output Mode:</p>
32+
<label class="radio-inline">
33+
<input type="radio" name="outputMode" value="composed" checked> Composed
34+
</label>
35+
<label class="radio-inline">
36+
<input type="radio" name="outputMode" value="individual"> Individual
37+
</label>
38+
</div>
39+
</fieldset>
40+
</form>
1841
<button class="btn btn-danger start">Start archiving</button>
1942
<button class="btn btn-success stop">Stop archiving</button>
2043
</div>

tests/test_archive.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import datetime
99
import pytz
1010

11-
from opentok import OpenTok, Archive, __version__
11+
from opentok import OpenTok, Archive, __version__, OutputModes
1212

1313
class OpenTokArchiveTest(unittest.TestCase):
1414
def setUp(self):
@@ -31,6 +31,7 @@ def test_stop_archive(self):
3131
u('status'): u('started'),
3232
u('hasAudio'): True,
3333
u('hasVideo'): True,
34+
u('outputMode'): OutputModes.composed.value,
3435
u('url'): None,
3536
})
3637
httpretty.register_uri(httpretty.POST, u('https://api.opentok.com/v2/partner/{0}/archive/{1}/stop').format(self.api_key, archive_id),
@@ -47,6 +48,7 @@ def test_stop_archive(self):
4748
"status" : "stopped",
4849
"hasAudio": true,
4950
"hasVideo": false,
51+
"outputMode": "composed",
5052
"url" : null
5153
}""")),
5254
status=200,
@@ -70,8 +72,9 @@ def test_stop_archive(self):
7072
expect(archive).to.have.property(u('created_at')).being.equal(created_at)
7173
expect(archive).to.have.property(u('size')).being.equal(0)
7274
expect(archive).to.have.property(u('duration')).being.equal(0)
73-
expect(archive).to.have.property(u('hasAudio')).being.equal(True)
74-
expect(archive).to.have.property(u('hasVideo')).being.equal(False)
75+
expect(archive).to.have.property(u('has_audio')).being.equal(True)
76+
expect(archive).to.have.property(u('has_video')).being.equal(False)
77+
expect(archive).to.have.property(u('output_mode')).being.equal(OutputModes.composed)
7578
expect(archive).to.have.property(u('url')).being.equal(None)
7679

7780
@httpretty.activate
@@ -89,6 +92,7 @@ def test_delete_archive(self):
8992
u('status'): u('available'),
9093
u('hasAudio'): True,
9194
u('hasVideo'): True,
95+
u('outputMode'): OutputModes.composed.value,
9296
u('url'): None,
9397
})
9498
httpretty.register_uri(httpretty.DELETE, u('https://api.opentok.com/v2/partner/{0}/archive/{1}').format(self.api_key, archive_id),

0 commit comments

Comments
 (0)