opentok 3.0.3 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/metrics.yml +17 -0
  3. data/.travis.yml +3 -1
  4. data/CODE_OF_CONDUCT.md +128 -0
  5. data/README.md +312 -28
  6. data/lib/opentok/archive.rb +45 -4
  7. data/lib/opentok/archives.rb +73 -5
  8. data/lib/opentok/broadcast.rb +118 -0
  9. data/lib/opentok/broadcasts.rb +149 -0
  10. data/lib/opentok/client.rb +212 -7
  11. data/lib/opentok/connections.rb +28 -0
  12. data/lib/opentok/constants.rb +1 -1
  13. data/lib/opentok/exceptions.rb +6 -0
  14. data/lib/opentok/opentok.rb +44 -10
  15. data/lib/opentok/session.rb +5 -0
  16. data/lib/opentok/signals.rb +47 -0
  17. data/lib/opentok/sip.rb +33 -0
  18. data/lib/opentok/stream.rb +46 -0
  19. data/lib/opentok/stream_list.rb +18 -0
  20. data/lib/opentok/streams.rb +79 -0
  21. data/lib/opentok/version.rb +1 -1
  22. data/opentok.gemspec +9 -8
  23. data/sample/Broadcast/Gemfile +4 -0
  24. data/sample/Broadcast/README.md +201 -0
  25. data/sample/Broadcast/broadcast_sample.rb +97 -0
  26. data/sample/Broadcast/public/css/sample.css +64 -0
  27. data/sample/Broadcast/public/js/host.js +185 -0
  28. data/sample/Broadcast/public/js/participant.js +85 -0
  29. data/sample/Broadcast/views/host.erb +82 -0
  30. data/sample/Broadcast/views/index.erb +32 -0
  31. data/sample/Broadcast/views/layout.erb +29 -0
  32. data/sample/Broadcast/views/participant.erb +27 -0
  33. data/sample/HelloWorld/public/js/helloworld.js +4 -10
  34. data/sample/HelloWorld/views/index.erb +1 -3
  35. data/spec/cassettes/OpenTok_Archives/calls_layout_on_archive_object.yml +47 -0
  36. data/spec/cassettes/OpenTok_Archives/changes_the_layout_of_an_archive.yml +38 -0
  37. data/spec/cassettes/OpenTok_Archives/http_client_errors/.yml +34 -0
  38. data/spec/cassettes/OpenTok_Archives/should_create_archives.yml +3 -1
  39. data/spec/cassettes/OpenTok_Archives/should_create_audio_only_archives.yml +3 -1
  40. data/spec/cassettes/OpenTok_Archives/should_create_custom_layout_archives.yml +50 -0
  41. data/spec/cassettes/OpenTok_Archives/should_create_hd_archives.yml +54 -0
  42. data/spec/cassettes/OpenTok_Archives/should_create_individual_archives.yml +3 -1
  43. data/spec/cassettes/OpenTok_Archives/should_create_named_archives.yml +3 -1
  44. data/spec/cassettes/OpenTok_Archives/should_delete_an_archive_by_id.yml +3 -1
  45. data/spec/cassettes/OpenTok_Archives/should_find_archives_by_id.yml +3 -1
  46. data/spec/cassettes/OpenTok_Archives/should_find_archives_with_unknown_properties.yml +3 -1
  47. data/spec/cassettes/OpenTok_Archives/should_find_expired_archives.yml +3 -1
  48. data/spec/cassettes/OpenTok_Archives/should_find_paused_archives_by_id.yml +3 -1
  49. data/spec/cassettes/OpenTok_Archives/should_stop_archives.yml +3 -1
  50. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_all_archives.yml +3 -1
  51. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_archives_with_an_offset.yml +3 -1
  52. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_count_number_of_archives.yml +3 -1
  53. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_part_of_the_archives_when_using_offset_and_count.yml +3 -1
  54. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_session_archives.yml +3 -1
  55. data/spec/cassettes/OpenTok_Broadcasts/calls_layout_on_broadcast_object.yml +57 -0
  56. data/spec/cassettes/OpenTok_Broadcasts/changes_the_layout_of_a_broadcast.yml +38 -0
  57. data/spec/cassettes/OpenTok_Broadcasts/fetches_a_hls_broadcast_url.yml +52 -0
  58. data/spec/cassettes/OpenTok_Broadcasts/finds_a_broadcast.yml +57 -0
  59. data/spec/cassettes/OpenTok_Broadcasts/starts_a_rtmp_broadcast.yml +61 -0
  60. data/spec/cassettes/OpenTok_Broadcasts/stops_a_broadcast.yml +47 -0
  61. data/spec/cassettes/OpenTok_Connections/forces_a_connection_to_be_terminated.yml +38 -0
  62. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_always_archived_sessions.yml +3 -1
  63. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_default_sessions.yml +3 -1
  64. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions.yml +3 -1
  65. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions_for_invalid_media_modes.yml +3 -1
  66. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions_with_a_location_hint.yml +3 -1
  67. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_routed_media_sessions.yml +3 -1
  68. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_routed_media_sessions_with_a_location_hint.yml +3 -1
  69. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_sessions_with_a_location_hint.yml +3 -1
  70. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/with_an_addendum_to_the_user_agent_string/should_append_the_addendum_to_the_user_agent_header.yml +3 -1
  71. data/spec/cassettes/OpenTok_Signals/receives_a_valid_response_for_a_connection.yml +39 -0
  72. data/spec/cassettes/OpenTok_Signals/receives_a_valid_response_for_all_connections.yml +39 -0
  73. data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +3 -1
  74. data/spec/cassettes/OpenTok_Streams/get_all_streams_information.yml +55 -0
  75. data/spec/cassettes/OpenTok_Streams/get_specific_stream_information.yml +44 -0
  76. data/spec/cassettes/OpenTok_Streams/layout_working_on_two_stream_list.yml +38 -0
  77. data/spec/opentok/archives_spec.rb +84 -1
  78. data/spec/opentok/broadcasts_spec.rb +171 -0
  79. data/spec/opentok/client_spec.rb +51 -0
  80. data/spec/opentok/connection_spec.rb +38 -0
  81. data/spec/opentok/opentok_spec.rb +21 -0
  82. data/spec/opentok/signal_spec.rb +50 -0
  83. data/spec/opentok/streams_spec.rb +75 -0
  84. data/spec/spec_helper.rb +2 -0
  85. metadata +84 -22
@@ -0,0 +1,64 @@
1
+ /* Move down content because we have a fixed navbar that is 50px tall */
2
+ body {
3
+ padding-top: 50px;
4
+ padding-bottom: 20px;
5
+ background-color: #F2F2F2;
6
+ }
7
+
8
+ /* Responsive: Portrait tablets and up */
9
+ @media screen and (min-width: 768px) {
10
+ /* Remove padding from wrapping element since we kick in the grid classes here */
11
+ .body-content {
12
+ padding: 0;
13
+ }
14
+ }
15
+
16
+ #streams {
17
+ background-color: gray;
18
+ width: 320px;
19
+ height: 180px;
20
+ }
21
+
22
+ #streams > div {
23
+ width: 20%;
24
+ height: 20%;
25
+ float: left;
26
+ position: relative;
27
+ cursor: pointer;
28
+ }
29
+
30
+ #streams.vertical > div {
31
+ left: 0px;
32
+ clear: left;
33
+ padding: 0px;
34
+ }
35
+
36
+ #streams .focus {
37
+ position: relative;
38
+ top: 0;
39
+ left: 0;
40
+ margin-top: 0;
41
+ height: 100%;
42
+ width: 100%;
43
+ }
44
+
45
+ #streams.vertical .focus {
46
+ padding: 0;
47
+ left: 0;
48
+ margin: 0;
49
+ left: 20%;
50
+ height: 100%;
51
+ width: 80%;
52
+ }
53
+
54
+ .stop {
55
+ display: none;
56
+ }
57
+
58
+ .bump-me {
59
+ padding-top: 40px;
60
+ }
61
+
62
+ .help-block {
63
+ font-weight: bold;
64
+ }
@@ -0,0 +1,185 @@
1
+ /* global OT, apiKey, sessionId, initialBroadcastId, token, $, initialLayout, focusStreamId */
2
+ /* eslint-disable no-console */
3
+
4
+ var session = OT.initSession(apiKey, sessionId);
5
+ var publisher = OT.initPublisher('publisher', {
6
+ insertMode: 'append',
7
+ width: '100%',
8
+ height: '100%',
9
+ resolution: '1280x720'
10
+ });
11
+ var broadcastId = initialBroadcastId;
12
+ var layout = initialLayout;
13
+
14
+ function disableForm() {
15
+ $('.broadcast-options-fields').attr('disabled', 'disabled');
16
+ $('.start').hide();
17
+ $('.stop').show();
18
+ }
19
+
20
+ function enableForm() {
21
+ $('.broadcast-options-fields').removeAttr('disabled');
22
+ $('.start').show();
23
+ $('.stop').hide();
24
+ }
25
+
26
+ function positionStreams() {
27
+ var $focusElement;
28
+ $focusElement = $('.focus');
29
+ if ($('#streams').hasClass('vertical')) {
30
+ $('#streams').children().css('top', '0');
31
+ $focusElement.appendTo('#streams');
32
+ $focusElement.css('top', (-20 * ($('#streams').children().size() - 1)) + '%');
33
+ }
34
+ else {
35
+ $focusElement.prependTo('#streams');
36
+ $focusElement.css('top', '0');
37
+ }
38
+ }
39
+
40
+ function setFocus(focusStreamId) {
41
+ var $focusElement;
42
+ var otherStreams = $.map($('#streams').children(), function (element) {
43
+ var streamId = (element.id === 'publisher' && publisher.stream) ? publisher.stream.streamId
44
+ : element.id;
45
+ if (streamId !== focusStreamId) {
46
+ $('#' + element.id).removeClass('focus');
47
+ return streamId;
48
+ }
49
+ return null;
50
+ });
51
+
52
+ $.post('/focus', {
53
+ focus: focusStreamId,
54
+ otherStreams: otherStreams
55
+ }).done(function () {
56
+ console.log('Focus changed.');
57
+ }).fail(function (jqXHR) {
58
+ console.error('Stream class list error:', jqXHR.responseText);
59
+ });
60
+
61
+ $('.focus').removeClass('focus');
62
+ $focusElement = (publisher.stream && publisher.stream.streamId === focusStreamId) ?
63
+ $('#publisher') : $('#' + focusStreamId);
64
+ $focusElement.addClass('focus');
65
+ session.signal({
66
+ type: 'focusStream',
67
+ data: focusStreamId
68
+ });
69
+ positionStreams();
70
+ }
71
+
72
+ function createFocusClick(elementId, focusStreamId) {
73
+ $('#' + elementId).click(function () {
74
+ setFocus(focusStreamId);
75
+ });
76
+ }
77
+
78
+ if (initialLayout === 'verticalPresentation') {
79
+ $('#streams').addClass('vertical');
80
+ }
81
+
82
+ if (initialLayout === 'verticalPresentation') {
83
+ $('.start').hide();
84
+ $('.stop').show();
85
+ }
86
+
87
+ session.connect(token, function (err) {
88
+ if (err) {
89
+ alert(err.message || err); // eslint-disable-line no-alert
90
+ }
91
+ session.publish(publisher);
92
+ });
93
+
94
+ publisher.on('streamCreated', function () {
95
+ createFocusClick(publisher.id, publisher.stream.streamId);
96
+ positionStreams();
97
+ });
98
+
99
+ session.on('streamCreated', function (event) {
100
+ var subscriber;
101
+ var streamId = event.stream.streamId;
102
+ var $streamContainer = $('<div></div>');
103
+ $streamContainer.attr('id', event.stream.id);
104
+ $('#streams').append($streamContainer);
105
+ subscriber = session.subscribe(event.stream, streamId, {
106
+ insertMode: 'append',
107
+ width: '100%',
108
+ height: '100%'
109
+ });
110
+
111
+ if (streamId === focusStreamId) {
112
+ setFocus(streamId);
113
+ }
114
+ createFocusClick(subscriber.id, streamId);
115
+ positionStreams();
116
+ });
117
+
118
+ session.on('streamDestroyed', function (event) {
119
+ var $streamElem = $('#' + event.stream.id);
120
+ if ($streamElem.hasClass('focus')) {
121
+ setFocus(publisher.stream.streamId);
122
+ }
123
+ $streamElem.remove();
124
+ positionStreams();
125
+ });
126
+
127
+ $(document).ready(function () {
128
+ $('.start').click(function () {
129
+ var options = {
130
+ maxDuration: $('input[name=maxDuration]').val() || undefined,
131
+ resolution: $('input[name=resolution]:checked').val(),
132
+ layout: {
133
+ type: layout
134
+ }
135
+ };
136
+ disableForm();
137
+ $.post('/start', options)
138
+ .done(function (response) {
139
+ console.log('start success.');
140
+ broadcastId = response.id;
141
+ setFocus(publisher.stream.streamId);
142
+ })
143
+ .fail(function (jqXHR) {
144
+ console.error(jqXHR.responseText);
145
+ enableForm();
146
+ });
147
+ }).prop('disabled', false);
148
+ $('.stop').click(function () {
149
+ $.get('stop/' + broadcastId)
150
+ .done(function () {
151
+ console.log('stop success.');
152
+ broadcastId = null;
153
+ enableForm();
154
+ })
155
+ .fail(function (jqXHR) {
156
+ console.error(jqXHR.responseText);
157
+ });
158
+ });
159
+ $('.toggle-layout').click(function () {
160
+ if ($('#streams').hasClass('vertical')) {
161
+ $('#streams').removeClass('vertical');
162
+ }
163
+ else {
164
+ $('#streams').addClass('vertical');
165
+ }
166
+
167
+ positionStreams();
168
+
169
+ layout = $('#streams').hasClass('vertical') ? 'verticalPresentation'
170
+ : 'horizontalPresentation';
171
+
172
+ $.post('broadcast/' + broadcastId + '/layout', {
173
+ type: layout
174
+ }).done(function () {
175
+ console.log('Broadcast layout updated.');
176
+ }).fail(function (jqXHR) {
177
+ console.error('Broadcast layout error:', jqXHR.responseText);
178
+ });
179
+
180
+ session.signal({
181
+ type: 'layoutClass',
182
+ data: layout
183
+ });
184
+ });
185
+ });
@@ -0,0 +1,85 @@
1
+ /* global OT, apiKey, sessionId, token, $, layout, focusStreamId */
2
+ var session = OT.initSession(apiKey, sessionId);
3
+ var publisher;
4
+
5
+ var container = $('<div id = "publisher"></div>');
6
+
7
+ if (layout === 'verticalPresentation') {
8
+ $('#streams').addClass('vertical');
9
+ }
10
+
11
+ container.addClass('focus');
12
+ $('#streams').append(container);
13
+
14
+ publisher = OT.initPublisher('publisher', {
15
+ insertMode: 'append',
16
+ width: '100%',
17
+ height: '100%',
18
+ resolution: '1280x720'
19
+ });
20
+
21
+ function positionStreams() {
22
+ var $focusElement = $('.focus');
23
+ if ($('#streams').hasClass('vertical')) {
24
+ $focusElement.appendTo('#streams');
25
+ $('#streams').children().css('top', '0');
26
+ $focusElement.css('top', (-20 * ($('#streams').children().size() - 1)) + '%');
27
+ }
28
+ else {
29
+ $focusElement.prependTo('#streams');
30
+ $focusElement.css('top', '0');
31
+ }
32
+ }
33
+
34
+ function focusStream(streamId) {
35
+ var focusStreamId = streamId;
36
+ var $focusElement = (publisher.stream && publisher.stream.id === focusStreamId) ? $('#publisher')
37
+ : $('#' + focusStreamId);
38
+ $('.focus').removeClass('focus');
39
+ $focusElement.addClass('focus');
40
+ positionStreams();
41
+ }
42
+
43
+ session.connect(token, function (err) {
44
+ if (err) {
45
+ alert(err.message || err); // eslint-disable-line no-alert
46
+ }
47
+ session.publish(publisher);
48
+ });
49
+
50
+ session.on('streamCreated', function (event) {
51
+ var streamId = event.stream.id;
52
+ container = document.createElement('div');
53
+ container.id = streamId;
54
+ $('#streams').append(container);
55
+ session.subscribe(event.stream, streamId, {
56
+ insertMode: 'append',
57
+ width: '100%',
58
+ height: '100%'
59
+ });
60
+ if (streamId === focusStreamId) {
61
+ focusStream(streamId);
62
+ }
63
+ positionStreams();
64
+ });
65
+
66
+ session.on('streamDestroyed', function (event) {
67
+ $('#' + event.stream.id).remove();
68
+ positionStreams();
69
+ });
70
+
71
+ session.on('signal:layoutClass', function (event) {
72
+ if (event.data === 'horizontalPresentation') {
73
+ $('#streams').removeClass('vertical');
74
+ $('.focus').prependTo('#streams');
75
+ }
76
+ else {
77
+ $('#streams').addClass('vertical');
78
+ $('.focus').appendTo('#streams');
79
+ }
80
+ positionStreams();
81
+ });
82
+
83
+ session.on('signal:focusStream', function (event) {
84
+ focusStream(event.data);
85
+ });
@@ -0,0 +1,82 @@
1
+
2
+ <script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
3
+
4
+ <div class="container bump-me">
5
+
6
+ <div class="body-content">
7
+
8
+ <div class="panel panel-default">
9
+ <div class="panel-heading">
10
+ <h3 class="panel-title">Host</h3>
11
+ </div>
12
+ <div class="panel-body">
13
+ <div id="streams">
14
+ <div id="publisher" class="focus"></div>
15
+ </div>
16
+ </div>
17
+ <div class="panel-footer">
18
+ <form class="broadcast-options">
19
+ <fieldset class="broadcast-options-fields">
20
+ <div class="form-group">
21
+ <h4>Broadcast Options:</h4>
22
+ <label>
23
+ Maximum duration (seconds, minimum 60, maximum 36000):
24
+ <input type="number" step="30" min="60" max="36000" name="maxDuration">
25
+ </label>
26
+ </div>
27
+
28
+ <div class="form-group">
29
+ <p>
30
+ <label>Resolution:</label>
31
+ <label class="help-block">
32
+ <input type="radio" name="resolution" value="640x480"> 640x480
33
+ </label>
34
+ <label class="help-block">
35
+ <input type="radio" name="resolution" value="1280x720" checked> 1280x720
36
+ </label>
37
+ </p>
38
+ </div>
39
+ </fieldset>
40
+ </form>
41
+ <button class="btn btn-danger start" disabled>Start broadcast</button>
42
+ <button class="btn btn-success stop">Stop broadcast</button>
43
+ <button class="btn toggle-layout">Toggle layout</button>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="panel panel-default">
49
+ <div class="panel-heading">
50
+ <h3 class="panel-title">Instructions</h3>
51
+ </div>
52
+ <div class="panel-body">
53
+ <p>
54
+ Click <strong>Start broadcast</strong> to start broadcasting this session.
55
+ All publishers in the session will be included, and all publishers that
56
+ join the session will be included as well.
57
+ </p>
58
+ <p>
59
+ Click <strong>Stop broadcast</strong> to stop broadcasting this session.
60
+ </p>
61
+ <p>
62
+ Click <strong>Toggle layout</strong> to toggle the layout
63
+ between a vertical and horizontal presentation. The layout changes in all clients
64
+ and in the broadcast.
65
+ <p>
66
+ Click any stream to set it to be the focus stream in the broadcast layout.
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <script>
72
+ var sessionId = "<%= sessionId %>";
73
+ var initialBroadcastId = "<%= initialBroadcastId || '' %>";
74
+ var apiKey = "<%= apiKey %>";
75
+ var token = "<%= token %>";
76
+ var focusStreamId = "<%= focusStreamId || '' %>";
77
+ var initialLayout = "<%= initialLayout %>";
78
+ </script>
79
+ <script src="/js/host.js"></script>
80
+
81
+ </div>
82
+
@@ -0,0 +1,32 @@
1
+ <div class="container bump-me">
2
+
3
+
4
+ <div class="body-content">
5
+
6
+
7
+ <div class="row">
8
+ <div class="col-lg-6 col-offset-1">
9
+
10
+ <div class="panel panel-default">
11
+ <div class="panel-heading">Create a broadcast</div>
12
+ <div class="panel-body">
13
+ <p>
14
+ Everyone who joins either the Host View or Participant View
15
+ joins a single OpenTok session. The Host can click
16
+ Start Broadcast and Stop Broadcast to control the live streaming
17
+ broadcast of the entire session.
18
+ </p>
19
+ </div>
20
+ <div class="panel-footer">
21
+ <a class="btn btn-danger" href="host">Host View</a>
22
+ <a class="btn btn-danger" href="participant">Participant View</a>
23
+ <a class="btn btn-danger" href="broadcast">Broadcast URL</a>
24
+ </div>
25
+ </div>
26
+
27
+ </div>
28
+
29
+ </div>
30
+
31
+ </div>
32
+ </div>