opentok 4.2.0 → 4.3.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -3
- data/CHANGES.md +153 -0
- data/lib/opentok/archive.rb +49 -2
- data/lib/opentok/archives.rb +89 -4
- data/lib/opentok/broadcast.rb +44 -1
- data/lib/opentok/broadcast_list.rb +14 -0
- data/lib/opentok/broadcasts.rb +109 -6
- data/lib/opentok/client.rb +161 -0
- data/lib/opentok/connections.rb +1 -1
- data/lib/opentok/constants.rb +1 -0
- data/lib/opentok/opentok.rb +7 -7
- data/lib/opentok/sip.rb +40 -2
- data/lib/opentok/streams.rb +50 -1
- data/lib/opentok/token_generator.rb +1 -0
- data/lib/opentok/version.rb +1 -1
- data/opentok.gemspec +1 -1
- data/sample/Broadcast/README.md +42 -0
- data/sample/Broadcast/broadcast_sample.rb +15 -0
- data/sample/Broadcast/views/all.erb +46 -0
- data/sample/Broadcast/views/index.erb +16 -1
- data/spec/cassettes/OpenTok_Archives/adds_a_stream_to_an_archive.yml +37 -0
- data/spec/cassettes/OpenTok_Archives/removes_a_stream_from_an_archive.yml +37 -0
- data/spec/cassettes/OpenTok_Broadcasts/adds_a_stream_to_a_broadcast.yml +37 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_all_broadcasts.yml +192 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_broadcasts_with_an_offset.yml +117 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_count_number_of_broadcasts.yml +92 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_part_of_the_broadcasts_when_using_offset_and_count.yml +142 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_session_broadcasts.yml +117 -0
- data/spec/cassettes/OpenTok_Broadcasts/removes_a_stream_from_a_broadcast.yml +37 -0
- data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_connection/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
- data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_session/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
- data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +2 -2
- data/spec/cassettes/OpenTok_Streams/disables_the_mute_state_of_a_session.yml +37 -0
- data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted.yml +39 -0
- data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted_except_specified_excluded_streams.yml +39 -0
- data/spec/cassettes/OpenTok_Streams/forces_the_specified_stream_to_be_muted.yml +39 -0
- data/spec/opentok/archives_spec.rb +11 -0
- data/spec/opentok/broadcasts_spec.rb +50 -3
- data/spec/opentok/connection_spec.rb +1 -1
- data/spec/opentok/sip_spec.rb +32 -1
- data/spec/opentok/streams_spec.rb +21 -1
- metadata +38 -5
data/lib/opentok/client.rb
CHANGED
@@ -188,6 +188,33 @@ module OpenTok
|
|
188
188
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
189
189
|
end
|
190
190
|
|
191
|
+
def select_streams_for_archive(archive_id, opts)
|
192
|
+
opts.extend(HashExtensions)
|
193
|
+
body = opts.camelize_keys!
|
194
|
+
response = self.class.patch("/v2/project/#{@api_key}/archive/#{archive_id}/streams", {
|
195
|
+
:body => body.to_json,
|
196
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
197
|
+
})
|
198
|
+
case response.code
|
199
|
+
when 204
|
200
|
+
response
|
201
|
+
when 400
|
202
|
+
raise OpenTokArchiveError, "The request was invalid."
|
203
|
+
when 403
|
204
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
205
|
+
when 404
|
206
|
+
raise OpenTokArchiveError, "No matching archive found with the specified ID: #{archive_id}"
|
207
|
+
when 405
|
208
|
+
raise OpenTokArchiveError, "The archive was started with streamMode set to 'auto', which does not support stream manipulation."
|
209
|
+
when 500
|
210
|
+
raise OpenTokError, "OpenTok server error."
|
211
|
+
else
|
212
|
+
raise OpenTokArchiveError, "The archive streams could not be updated."
|
213
|
+
end
|
214
|
+
rescue StandardError => e
|
215
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
216
|
+
end
|
217
|
+
|
191
218
|
def forceDisconnect(session_id, connection_id)
|
192
219
|
response = self.class.delete("/v2/project/#{@api_key}/session/#{session_id}/connection/#{connection_id}", {
|
193
220
|
:headers => generate_headers("Content-Type" => "application/json")
|
@@ -206,6 +233,45 @@ module OpenTok
|
|
206
233
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
207
234
|
end
|
208
235
|
|
236
|
+
def force_mute_stream(session_id, stream_id)
|
237
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/stream/#{stream_id}/mute", {
|
238
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
239
|
+
})
|
240
|
+
case response.code
|
241
|
+
when 200
|
242
|
+
response
|
243
|
+
when 400
|
244
|
+
raise ArgumentError, "Force mute failed. Stream ID #{stream_id} or Session ID #{session_id} is invalid"
|
245
|
+
when 403
|
246
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
247
|
+
when 404
|
248
|
+
raise OpenTokConnectionError, "Either Stream ID #{stream_id} or Session ID #{session_id} is invalid"
|
249
|
+
end
|
250
|
+
rescue StandardError => e
|
251
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
252
|
+
end
|
253
|
+
|
254
|
+
def force_mute_session(session_id, opts)
|
255
|
+
opts.extend(HashExtensions)
|
256
|
+
body = opts.camelize_keys!
|
257
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/mute", {
|
258
|
+
:body => body.to_json,
|
259
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
260
|
+
})
|
261
|
+
case response.code
|
262
|
+
when 200
|
263
|
+
response
|
264
|
+
when 400
|
265
|
+
raise ArgumentError, "Force mute failed. The request could not be processed due to a bad request"
|
266
|
+
when 403
|
267
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
268
|
+
when 404
|
269
|
+
raise OpenTokConnectionError, "Session ID #{session_id} is invalid"
|
270
|
+
end
|
271
|
+
rescue StandardError => e
|
272
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
273
|
+
end
|
274
|
+
|
209
275
|
def signal(session_id, connection_id, opts)
|
210
276
|
opts.extend(HashExtensions)
|
211
277
|
connectionPath = connection_id.to_s.empty? ? "" : "/connection/#{connection_id}"
|
@@ -257,6 +323,52 @@ module OpenTok
|
|
257
323
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
258
324
|
end
|
259
325
|
|
326
|
+
def play_dtmf_to_connection(session_id, connection_id, dtmf_digits)
|
327
|
+
body = { "digits" => dtmf_digits }
|
328
|
+
|
329
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/connection/#{connection_id}/play-dtmf", {
|
330
|
+
:body => body.to_json,
|
331
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
332
|
+
})
|
333
|
+
case response.code
|
334
|
+
when 200
|
335
|
+
response
|
336
|
+
when 400
|
337
|
+
raise ArgumentError, "One of the properties — dtmf_digits #{dtmf_digits} or session_id #{session_id} — is invalid."
|
338
|
+
when 403
|
339
|
+
raise OpenTokAuthenticationError, "Authentication failed. This can occur if you use an invalid OpenTok API key or an invalid JSON web token. API Key: #{@api_key}"
|
340
|
+
when 404
|
341
|
+
raise OpenTokError, "The specified session #{session_id} does not exist or the client specified by the #{connection_id} property is not connected to the session."
|
342
|
+
else
|
343
|
+
raise OpenTokError, "An error occurred when attempting to play DTMF digits to the session"
|
344
|
+
end
|
345
|
+
rescue StandardError => e
|
346
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
347
|
+
end
|
348
|
+
|
349
|
+
def play_dtmf_to_session(session_id, dtmf_digits)
|
350
|
+
body = { "digits" => dtmf_digits }
|
351
|
+
|
352
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/play-dtmf", {
|
353
|
+
:body => body.to_json,
|
354
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
355
|
+
})
|
356
|
+
case response.code
|
357
|
+
when 200
|
358
|
+
response
|
359
|
+
when 400
|
360
|
+
raise ArgumentError, "One of the properties — dtmf_digits #{dtmf_digits} or session_id #{session_id} — is invalid."
|
361
|
+
when 403
|
362
|
+
raise OpenTokAuthenticationError, "Authentication failed. This can occur if you use an invalid OpenTok API key or an invalid JSON web token. API Key: #{@api_key}"
|
363
|
+
when 404
|
364
|
+
raise OpenTokError, "The specified session does not exist. Session ID: #{session_id}"
|
365
|
+
else
|
366
|
+
raise OpenTokError, "An error occurred when attempting to play DTMF digits to the session"
|
367
|
+
end
|
368
|
+
rescue StandardError => e
|
369
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
370
|
+
end
|
371
|
+
|
260
372
|
def info_stream(session_id, stream_id)
|
261
373
|
streamId = stream_id.to_s.empty? ? '' : "/#{stream_id}"
|
262
374
|
url = "/v2/project/#{@api_key}/session/#{session_id}/stream#{streamId}"
|
@@ -371,6 +483,28 @@ module OpenTok
|
|
371
483
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
372
484
|
end
|
373
485
|
|
486
|
+
def list_broadcasts(offset, count, session_id)
|
487
|
+
query = Hash.new
|
488
|
+
query[:offset] = offset unless offset.nil?
|
489
|
+
query[:count] = count unless count.nil?
|
490
|
+
query[:sessionId] = session_id unless session_id.nil?
|
491
|
+
response = self.class.get("/v2/project/#{@api_key}/broadcast", {
|
492
|
+
:query => query.empty? ? nil : query,
|
493
|
+
:headers => generate_headers,
|
494
|
+
})
|
495
|
+
case response.code
|
496
|
+
when 200
|
497
|
+
response
|
498
|
+
when 403
|
499
|
+
raise OpenTokAuthenticationError,
|
500
|
+
"Authentication failed while retrieving broadcasts. API Key: #{@api_key}"
|
501
|
+
else
|
502
|
+
raise OpenTokBroadcastError, "The broadcasts could not be retrieved."
|
503
|
+
end
|
504
|
+
rescue StandardError => e
|
505
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
506
|
+
end
|
507
|
+
|
374
508
|
def layout_broadcast(broadcast_id, opts)
|
375
509
|
opts.extend(HashExtensions)
|
376
510
|
response = self.class.put("/v2/project/#{@api_key}/broadcast/#{broadcast_id}/layout", {
|
@@ -393,5 +527,32 @@ module OpenTok
|
|
393
527
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
394
528
|
end
|
395
529
|
|
530
|
+
def select_streams_for_broadcast(broadcast_id, opts)
|
531
|
+
opts.extend(HashExtensions)
|
532
|
+
body = opts.camelize_keys!
|
533
|
+
response = self.class.patch("/v2/project/#{@api_key}/broadcast/#{broadcast_id}/streams", {
|
534
|
+
:body => body.to_json,
|
535
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
536
|
+
})
|
537
|
+
case response.code
|
538
|
+
when 204
|
539
|
+
response
|
540
|
+
when 400
|
541
|
+
raise OpenTokBroadcastError, "The request was invalid."
|
542
|
+
when 403
|
543
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
544
|
+
when 404
|
545
|
+
raise OpenTokBroadcastError, "No matching broadcast found with the specified ID: #{broadcast_id}"
|
546
|
+
when 405
|
547
|
+
raise OpenTokBroadcastError, "The broadcast was started with streamMode set to 'auto', which does not support stream manipulation."
|
548
|
+
when 500
|
549
|
+
raise OpenTokError, "OpenTok server error."
|
550
|
+
else
|
551
|
+
raise OpenTokBroadcastError, "The broadcast streams could not be updated."
|
552
|
+
end
|
553
|
+
rescue StandardError => e
|
554
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
555
|
+
end
|
556
|
+
|
396
557
|
end
|
397
558
|
end
|
data/lib/opentok/connections.rb
CHANGED
data/lib/opentok/constants.rb
CHANGED
data/lib/opentok/opentok.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require "resolv"
|
2
|
+
require "set"
|
3
|
+
|
1
4
|
require "opentok/constants"
|
2
5
|
require "opentok/session"
|
3
6
|
require "opentok/client"
|
@@ -9,9 +12,6 @@ require "opentok/streams"
|
|
9
12
|
require "opentok/signals"
|
10
13
|
require "opentok/broadcasts"
|
11
14
|
|
12
|
-
require "resolv"
|
13
|
-
require "set"
|
14
|
-
|
15
15
|
module OpenTok
|
16
16
|
# Contains methods for creating OpenTok sessions and generating tokens. It also includes
|
17
17
|
# methods for returning object that let you work with archives, work with live streaming
|
@@ -42,9 +42,9 @@ module OpenTok
|
|
42
42
|
# streams, and signal. (This is the default value if you do not specify a role.)
|
43
43
|
#
|
44
44
|
# * <code>:moderator</code> -- In addition to the privileges granted to a
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
45
|
+
# publisher, a moderator can perform moderation functions, such as forcing clients
|
46
|
+
# to disconnect, to stop publishing streams, or to mute audio in published streams. See the
|
47
|
+
# {https://tokbox.com/developer/guides/moderation/ Moderation developer guide}.
|
48
48
|
# @option options [integer] :expire_time The expiration time, in seconds since the UNIX epoch.
|
49
49
|
# Pass in 0 to use the default expiration time of 24 hours after the token creation time.
|
50
50
|
# The maximum expiration time is 30 days after the creation time.
|
@@ -207,7 +207,7 @@ module OpenTok
|
|
207
207
|
@signals ||= Signals.new client
|
208
208
|
end
|
209
209
|
|
210
|
-
# A Connections object, which lets disconnect clients from an OpenTok session.
|
210
|
+
# A Connections object, which lets you disconnect clients from an OpenTok session.
|
211
211
|
def connections
|
212
212
|
@connections ||= Connections.new client
|
213
213
|
end
|
data/lib/opentok/sip.rb
CHANGED
@@ -12,7 +12,9 @@ module OpenTok
|
|
12
12
|
# "password" => sip_password },
|
13
13
|
# "headers" => { "X-KEY1" => "value1",
|
14
14
|
# "X-KEY1" => "value2" },
|
15
|
-
# "secure" => "true"
|
15
|
+
# "secure" => "true",
|
16
|
+
# "video" => "true",
|
17
|
+
# "observe_force_mute" => "true"
|
16
18
|
# }
|
17
19
|
# response = opentok.sip.dial(session_id, token, "sip:+15128675309@acme.pstn.example.com;transport=tls", opts)
|
18
20
|
# @param [String] session_id The session ID corresponding to the session to which
|
@@ -33,14 +35,50 @@ module OpenTok
|
|
33
35
|
# @option opts [Hash] :auth This object contains the username and password
|
34
36
|
# to be used in the the SIP INVITE request for HTTP digest authentication,
|
35
37
|
# if it is required by your SIP platform.
|
36
|
-
# @option opts [true, false] :secure
|
38
|
+
# @option opts [true, false] :secure Whether the media must be transmitted
|
37
39
|
# encrypted (true) or not (false, the default).
|
40
|
+
# @option opts [true, false] :video Whether the SIP call will include
|
41
|
+
# video (true) or not (false, the default). With video included, the SIP
|
42
|
+
# client's video is included in the OpenTok stream that is sent to the
|
43
|
+
# OpenTok session. The SIP client will receive a single composed video of
|
44
|
+
# the published streams in the OpenTok session.
|
45
|
+
# @option opts [true, false] :observe_force_mute Whether the SIP end point
|
46
|
+
# observes {https://tokbox.com/developer/guides/moderation/#force_mute force mute moderation}
|
47
|
+
# (true) or not (false, the default).
|
38
48
|
def dial(session_id, token, sip_uri, opts)
|
39
49
|
response = @client.dial(session_id, token, sip_uri, opts)
|
40
50
|
end
|
41
51
|
|
52
|
+
# Sends DTMF digits to a specific client connected to an OpnTok session.
|
53
|
+
#
|
54
|
+
# @param [String] session_id The session ID.
|
55
|
+
# @param [String] connection_id The connection ID of the specific connection that
|
56
|
+
# the DTMF signal is being sent to.
|
57
|
+
# @param [String] dtmf_digits The DTMF digits to send. This can include 0-9, "*", "#", and "p".
|
58
|
+
# A p indicates a pause of 500ms (if you need to add a delay in sending the digits).
|
59
|
+
def play_dtmf_to_connection(session_id, connection_id, dtmf_digits)
|
60
|
+
raise ArgumentError, "invalid DTMF digits" unless dtmf_digits_valid?(dtmf_digits)
|
61
|
+
response = @client.play_dtmf_to_connection(session_id, connection_id, dtmf_digits)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sends DTMF digits to all clients connected to an OpnTok session.
|
65
|
+
#
|
66
|
+
# @param [String] session_id The session ID.
|
67
|
+
# @param [String] dtmf_digits The DTMF digits to send. This can include 0-9, "*", "#", and "p".
|
68
|
+
# A p indicates a pause of 500ms (if you need to add a delay in sending the digits).
|
69
|
+
def play_dtmf_to_session(session_id, dtmf_digits)
|
70
|
+
raise ArgumentError, "invalid DTMF digits" unless dtmf_digits_valid?(dtmf_digits)
|
71
|
+
response = @client.play_dtmf_to_session(session_id, dtmf_digits)
|
72
|
+
end
|
73
|
+
|
42
74
|
def initialize(client)
|
43
75
|
@client = client
|
44
76
|
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def dtmf_digits_valid?(dtmf_digits)
|
81
|
+
dtmf_digits.match?(/^[0-9*#p]+$/)
|
82
|
+
end
|
45
83
|
end
|
46
84
|
end
|
data/lib/opentok/streams.rb
CHANGED
@@ -75,5 +75,54 @@ module OpenTok
|
|
75
75
|
(200..300).include? response.code
|
76
76
|
end
|
77
77
|
|
78
|
+
# Force a specific stream connected to an OpenTok session to mute itself.
|
79
|
+
#
|
80
|
+
# @param [String] session_id The session ID of the OpenTok session.
|
81
|
+
# @param [String] stream_id The stream ID of the stream in the session.
|
82
|
+
#
|
83
|
+
def force_mute(session_id, stream_id)
|
84
|
+
response = @client.force_mute_stream(session_id, stream_id)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Force all streams connected to an OpenTok session (except for an optional list of streams),
|
88
|
+
# to mute published audio.
|
89
|
+
#
|
90
|
+
# In addition to existing streams, any streams that are published after the call to
|
91
|
+
# this method are published with audio muted. You can remove the mute state of a session
|
92
|
+
# by calling the disable_force_mute() method.
|
93
|
+
#
|
94
|
+
# @param [String] session_id The session ID.
|
95
|
+
# @param [Hash] opts An optional hash defining options for muting action. For example:
|
96
|
+
# @option opts [Array] :excluded_streams The stream IDs for streams that should not be muted.
|
97
|
+
# This is an optional property. If you omit this property, all streams in the session will be muted.
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# {
|
101
|
+
# "excluded_streams" => [
|
102
|
+
# "excludedStreamId1",
|
103
|
+
# "excludedStreamId2"
|
104
|
+
# ]
|
105
|
+
# }
|
106
|
+
#
|
107
|
+
def force_mute_all(session_id, opts = {})
|
108
|
+
opts['active'] = 'true'
|
109
|
+
response = @client.force_mute_session(session_id, opts)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Disables the active mute state of the session. After you call this method, new streams
|
113
|
+
# published to the session will no longer have audio muted.
|
114
|
+
#
|
115
|
+
# After you call the force_mute_all() method, any streams published after
|
116
|
+
# the call are published with audio muted. Call the disable_force_mute() method
|
117
|
+
# to remove the mute state of a session, so that new published streams are not
|
118
|
+
# automatically muted.
|
119
|
+
#
|
120
|
+
# @param [String] session_id The session ID.
|
121
|
+
#
|
122
|
+
def disable_force_mute(session_id)
|
123
|
+
opts = {'active' => 'false'}
|
124
|
+
response = @client.force_mute_session(session_id, opts)
|
125
|
+
end
|
126
|
+
|
78
127
|
end
|
79
|
-
end
|
128
|
+
end
|
data/lib/opentok/version.rb
CHANGED
data/opentok.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
|
19
19
|
bundler_version = RUBY_VERSION < '2.1' ? '~> 1.5' : '>= 1.5'
|
20
20
|
spec.add_development_dependency "bundler", bundler_version
|
21
|
-
spec.add_development_dependency "rake", "~> 12.
|
21
|
+
spec.add_development_dependency "rake", "~> 12.3.3"
|
22
22
|
spec.add_development_dependency "rspec", "~> 3.9.0"
|
23
23
|
spec.add_development_dependency "webmock", ">= 2.3.2"
|
24
24
|
spec.add_development_dependency "vcr", ">= 2.8.0"
|
data/sample/Broadcast/README.md
CHANGED
@@ -168,6 +168,48 @@ You will now see the participant in the broadcast.
|
|
168
168
|
end
|
169
169
|
```
|
170
170
|
|
171
|
+
### All broadcasts
|
172
|
+
|
173
|
+
Start by visiting the list page at <http://localhost:4567/all>. You will see a table that
|
174
|
+
displays all the broadcasts created with your API Key. If there are more than five, the next ones
|
175
|
+
can be seen by clicking the "Next →" link. Some basic information like
|
176
|
+
when the broadcast was created, how long it is, and its status is also shown. You should see the
|
177
|
+
broadcasts you created in the previous sections here.
|
178
|
+
|
179
|
+
We begin to see how this page is created by looking at the route handler for this URL:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
get '/all' do
|
183
|
+
page = (params[:page] || "1").to_i
|
184
|
+
offset = (page - 1) * 5
|
185
|
+
broadcasts = settings.opentok.broadcasts.all(:offset => offset, :count => 5)
|
186
|
+
|
187
|
+
show_previous = page > 1 ? '/all?page=' + (page-1).to_s : nil
|
188
|
+
show_next = broadcasts.total > (offset + 5) ? '/all?page=' + (page+1).to_s : nil
|
189
|
+
|
190
|
+
erb :all, :locals => {
|
191
|
+
:broadcasts => broadcasts,
|
192
|
+
:show_previous => show_previous,
|
193
|
+
:show_next => show_next
|
194
|
+
}
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
198
|
+
This view is paginated so that we don't potentially show hundreds of rows on the table, which would
|
199
|
+
be difficult for the user to navigate. So this code starts by figuring out which page needs to be
|
200
|
+
shown, where each page is a set of 5 broadcasts. The `page` number is read from the request's query
|
201
|
+
string parameters as a string and then converted into an Integer. The `offset`, which represents how
|
202
|
+
many broadcasts are being skipped is always calculated as five times as many pages that are less than
|
203
|
+
the current page, which is `(page - 1) * 5`. Now there is enough information to ask for a list of
|
204
|
+
broadcasts from OpenTok, which we do by calling the `broadcasts.all()` method of the `opentok` instance.
|
205
|
+
The parameter is an optional Hash that contains the offset, the count (which is always 5 in this
|
206
|
+
view). If we are not at the first page, we can pass the view a string that contains the relative URL
|
207
|
+
for the previous page. Similarly, we can also include one for the next page. Now the application
|
208
|
+
renders the view using that information and the partial list of broadcasts.
|
209
|
+
|
210
|
+
At this point the template file `views/all.erb` handles looping over the array of broadcasts and
|
211
|
+
outputting the proper information for each column in the table.
|
212
|
+
|
171
213
|
### Changing the layout classes for streams
|
172
214
|
|
173
215
|
In the host page, if you click on either the host or a participant video, that video gets
|
@@ -46,6 +46,21 @@ class BroadcastSample < Sinatra::Base
|
|
46
46
|
}
|
47
47
|
end
|
48
48
|
|
49
|
+
get '/all' do
|
50
|
+
page = (params[:page] || "1").to_i
|
51
|
+
offset = (page - 1) * 5
|
52
|
+
broadcasts = settings.opentok.broadcasts.all(:offset => offset, :count => 5)
|
53
|
+
|
54
|
+
show_previous = page > 1 ? '/all?page=' + (page-1).to_s : nil
|
55
|
+
show_next = broadcasts.total > (offset + 5) ? '/all?page=' + (page+1).to_s : nil
|
56
|
+
|
57
|
+
erb :all, :locals => {
|
58
|
+
:broadcasts => broadcasts,
|
59
|
+
:show_previous => show_previous,
|
60
|
+
:show_next => show_next
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
49
64
|
post '/start' do
|
50
65
|
opts = {
|
51
66
|
:maxDuration => params.key?("maxDuration") ? params[:maxDuration] : 7200,
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<div class="container bump-me">
|
2
|
+
|
3
|
+
<div class="body-content">
|
4
|
+
|
5
|
+
<div class="panel panel-default">
|
6
|
+
<div class="panel-heading">
|
7
|
+
<h3 class="panel-title">Broadcasts List</h3>
|
8
|
+
</div>
|
9
|
+
<div class="panel-body">
|
10
|
+
<% if broadcasts.count > 0 %>
|
11
|
+
<table class="table">
|
12
|
+
<thead>
|
13
|
+
<tr>
|
14
|
+
<th>Created</th>
|
15
|
+
<th>Status</th>
|
16
|
+
</tr>
|
17
|
+
</thead>
|
18
|
+
<tbody>
|
19
|
+
<% for item in broadcasts %>
|
20
|
+
|
21
|
+
<tr data-item-id="<%= item.id %>">
|
22
|
+
<td><%= Time.at(item.created_at/1000).strftime("%B %e, %Y at %I:%M %p") %></td>
|
23
|
+
<td><%= item.status %></td>
|
24
|
+
</tr>
|
25
|
+
|
26
|
+
<% end %>
|
27
|
+
</tbody>
|
28
|
+
</table>
|
29
|
+
<% else %>
|
30
|
+
<p>
|
31
|
+
There are no broadcasts currently. Try making one in the <a href="/host">host view</a>.
|
32
|
+
</p>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
<div class="panel-footer">
|
36
|
+
<% if show_previous %>
|
37
|
+
<a href="<%= show_previous %>" class="pull-left">← Previous</a>
|
38
|
+
<% end %>
|
39
|
+
|
40
|
+
<% if show_next %>
|
41
|
+
<a href="<%= show_next %>" class="pull-right">Next →</a>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
@@ -25,8 +25,23 @@
|
|
25
25
|
</div>
|
26
26
|
|
27
27
|
</div>
|
28
|
+
<div class="col-lg-6 col-offset-1">
|
29
|
+
|
30
|
+
<div class="panel panel-default">
|
31
|
+
<div class="panel-heading">List of Broadcasts</div>
|
32
|
+
<div class="panel-body">
|
33
|
+
<p>
|
34
|
+
Click through to List of Broadcasts to see examples of using the
|
35
|
+
Broadcasting REST API to list broadcasts showing status (started,
|
36
|
+
stopped, available) and created at timestamp.
|
37
|
+
</p>
|
38
|
+
</div>
|
39
|
+
<div class="panel-footer">
|
40
|
+
<a class="btn btn-success" href="all">List of Broadcasts</a>
|
41
|
+
</div>
|
42
|
+
</div>
|
28
43
|
|
29
44
|
</div>
|
30
45
|
|
31
46
|
</div>
|
32
|
-
</div>
|
47
|
+
</div>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: patch
|
5
|
+
uri: https://api.opentok.com/v2/project/123456/archive/30b3ebf1-ba36-4f5b-8def-6f70d9986fe9/streams
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"addStream":"12312312-3811-4726-b508-e41a0f96c68f"}'
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- OpenTok-Ruby-SDK/<%= version %>
|
12
|
+
X-Opentok-Auth:
|
13
|
+
- eyJpc3QiOiJwcm9qZWN0IiwiYWxnIjoiSFMyNTYifQ.eyJpc3MiOiIxMjM0NTYiLCJpYXQiOjE0OTI1MTA2NjAsImV4cCI6MTQ5MjUxMDk2MH0.BplMVhJWx4ld7KLKXqEmow6MjNPPFw9W8IHCMfeb120
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 204
|
23
|
+
message: No Content
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- nginx
|
27
|
+
Date:
|
28
|
+
- Tue, 23 Nov 2021 20:55:02 GMT
|
29
|
+
Connection:
|
30
|
+
- keep-alive
|
31
|
+
Set-Cookie:
|
32
|
+
- AWSALBTG=DuFUMmWLYpV+WXygU/vIF2GeEgO0ltb32OLXKREBQ059UXSGx826MWScm3+Iba9R/DrIu8wi8puQQB6HMDNNA5twML8KpmSno/CfRqigVEW7z7njwLdBeIjKowkK2oDiRCpWwn35G08EcNtdzJZUUO+Cqt/7MLfakD0RjBHzn0k0H6uk8Y4=;
|
33
|
+
Expires=Tue, 30 Nov 2021 20:55:02 GMT; Path=/
|
34
|
+
- AWSALBTGCORS=DuFUMmWLYpV+WXygU/vIF2GeEgO0ltb32OLXKREBQ059UXSGx826MWScm3+Iba9R/DrIu8wi8puQQB6HMDNNA5twML8KpmSno/CfRqigVEW7z7njwLdBeIjKowkK2oDiRCpWwn35G08EcNtdzJZUUO+Cqt/7MLfakD0RjBHzn0k0H6uk8Y4=;
|
35
|
+
Expires=Tue, 30 Nov 2021 20:55:02 GMT; Path=/; SameSite=None; Secure
|
36
|
+
recorded_at: Tue, 18 Apr 2017 10:17:40 GMT
|
37
|
+
recorded_with: VCR 6.0.0
|
@@ -0,0 +1,37 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: patch
|
5
|
+
uri: https://api.opentok.com/v2/project/123456/archive/30b3ebf1-ba36-4f5b-8def-6f70d9986fe9/streams
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"removeStream":"12312312-3811-4726-b508-e41a0f96c68f"}'
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- OpenTok-Ruby-SDK/<%= version %>
|
12
|
+
X-Opentok-Auth:
|
13
|
+
- eyJpc3QiOiJwcm9qZWN0IiwiYWxnIjoiSFMyNTYifQ.eyJpc3MiOiIxMjM0NTYiLCJpYXQiOjE0OTI1MTA2NjAsImV4cCI6MTQ5MjUxMDk2MH0.BplMVhJWx4ld7KLKXqEmow6MjNPPFw9W8IHCMfeb120
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 204
|
23
|
+
message: No Content
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- nginx
|
27
|
+
Date:
|
28
|
+
- Tue, 23 Nov 2021 20:55:03 GMT
|
29
|
+
Connection:
|
30
|
+
- keep-alive
|
31
|
+
Set-Cookie:
|
32
|
+
- AWSALBTG=C8HtbnHIrXFPcTucK+6zVh2r0MMrr/1Jb/WUzPKk5rAEw3LbnvwxrsMlwebeHFPZUj+lcnVMLZU91OiujdiYEUH5eBovMivdAk9Gd/O9f4iPju70zwCPrRaoMZnH3sFK6a8lG3Q966pXth4vEsvTA12tADiC2emPHC51Z75R51aQMKUxFPc=;
|
33
|
+
Expires=Tue, 30 Nov 2021 20:55:03 GMT; Path=/
|
34
|
+
- AWSALBTGCORS=C8HtbnHIrXFPcTucK+6zVh2r0MMrr/1Jb/WUzPKk5rAEw3LbnvwxrsMlwebeHFPZUj+lcnVMLZU91OiujdiYEUH5eBovMivdAk9Gd/O9f4iPju70zwCPrRaoMZnH3sFK6a8lG3Q966pXth4vEsvTA12tADiC2emPHC51Z75R51aQMKUxFPc=;
|
35
|
+
Expires=Tue, 30 Nov 2021 20:55:03 GMT; Path=/; SameSite=None; Secure
|
36
|
+
recorded_at: Tue, 18 Apr 2017 10:17:40 GMT
|
37
|
+
recorded_with: VCR 6.0.0
|
@@ -0,0 +1,37 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: patch
|
5
|
+
uri: https://api.opentok.com/v2/project/123456/broadcast/13dbcc23-af92-4862-9184-74b21815a814/streams
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"addStream":"12312312-3811-4726-b508-e41a0f96c68f"}'
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- OpenTok-Ruby-SDK/<%= version %>
|
12
|
+
X-Opentok-Auth:
|
13
|
+
- eyJpc3QiOiJwcm9qZWN0IiwiYWxnIjoiSFMyNTYifQ.eyJpc3MiOiIxMjM0NTYiLCJpYXQiOjE0OTI1MTA2NjAsImV4cCI6MTQ5MjUxMDk2MH0.BplMVhJWx4ld7KLKXqEmow6MjNPPFw9W8IHCMfeb120
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 204
|
23
|
+
message: No Content
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- nginx
|
27
|
+
Date:
|
28
|
+
- Tue, 23 Nov 2021 20:44:18 GMT
|
29
|
+
Connection:
|
30
|
+
- keep-alive
|
31
|
+
Set-Cookie:
|
32
|
+
- AWSALBTG=Nj5lPQS/JnLA1JkA1AcUDSrn5vUxVtJ5srP+UHP/scSFLccYUClejVswu+NTMqGisdJi/7X/qdPMWBEq6ReBXCALUGz+NKunxs/WrIBFxBOz6EYgWkHXh8DY9Rtx2xEQmDzdCDqXIgr1aUzTv8J3taybWQm4yBpzHjFJFoUjpNYxS6+1zlI=;
|
33
|
+
Expires=Tue, 30 Nov 2021 20:44:18 GMT; Path=/
|
34
|
+
- AWSALBTGCORS=Nj5lPQS/JnLA1JkA1AcUDSrn5vUxVtJ5srP+UHP/scSFLccYUClejVswu+NTMqGisdJi/7X/qdPMWBEq6ReBXCALUGz+NKunxs/WrIBFxBOz6EYgWkHXh8DY9Rtx2xEQmDzdCDqXIgr1aUzTv8J3taybWQm4yBpzHjFJFoUjpNYxS6+1zlI=;
|
35
|
+
Expires=Tue, 30 Nov 2021 20:44:18 GMT; Path=/; SameSite=None; Secure
|
36
|
+
recorded_at: Tue, 18 Apr 2017 10:17:40 GMT
|
37
|
+
recorded_with: VCR 6.0.0
|