opentok 4.1.0 → 4.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/.github/workflows/metrics.yml +17 -0
- data/CHANGES.md +171 -0
- data/README.md +62 -38
- data/lib/opentok/archive.rb +54 -4
- data/lib/opentok/archives.rb +115 -11
- data/lib/opentok/broadcast.rb +50 -3
- data/lib/opentok/broadcast_list.rb +14 -0
- data/lib/opentok/broadcasts.rb +178 -19
- data/lib/opentok/client.rb +251 -0
- data/lib/opentok/connections.rb +1 -1
- data/lib/opentok/constants.rb +1 -0
- data/lib/opentok/exceptions.rb +3 -1
- data/lib/opentok/opentok.rb +16 -10
- data/lib/opentok/render.rb +78 -0
- data/lib/opentok/render_list.rb +14 -0
- data/lib/opentok/renders.rb +101 -0
- data/lib/opentok/session.rb +4 -4
- data/lib/opentok/sip.rb +40 -2
- data/lib/opentok/streams.rb +49 -2
- data/lib/opentok/token_generator.rb +1 -0
- data/lib/opentok/version.rb +1 -1
- data/opentok.gemspec +2 -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_Archives/should_create_an_archive_with_matching_multi_archive_tag_when_multiArchiveTag_is_specified.yml +50 -0
- data/spec/cassettes/OpenTok_Archives/should_create_an_archive_with_multi_archive_tag_value_of_nil_when_multiArchiveTag_not_specified.yml +49 -0
- data/spec/cassettes/OpenTok_Archives/should_create_an_archives_with_a_specified_multiArchiveTag.yml +52 -0
- data/spec/cassettes/OpenTok_Archives/should_create_layout_archives_with_screenshare_layout_types.yml +50 -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_Broadcasts/starts_a_broadcast_with_a_matching_multi_broadcast_tag_value_when_multiBroadcastTag_is_specified.yml +54 -0
- data/spec/cassettes/OpenTok_Broadcasts/starts_a_broadcast_with_a_multi_broadcast_tag_value_of_nil_when_multiBroadcastTag_not_specified.yml +53 -0
- data/spec/cassettes/OpenTok_Broadcasts/starts_a_broadcast_with_a_specified_multiBroadcastTag.yml +54 -0
- data/spec/cassettes/OpenTok_Renders/finds_an_Experience_Composer_render.yml +46 -0
- data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_all_renders.yml +108 -0
- data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_count_number_of_renders.yml +63 -0
- data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_part_of_the_renders_when_using_offset_and_count.yml +63 -0
- data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_renders_with_an_offset.yml +74 -0
- data/spec/cassettes/OpenTok_Renders/starts_an_Experience_Composer_render.yml +48 -0
- data/spec/cassettes/OpenTok_Renders/stops_an_Experience_Composer_render.yml +28 -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 +61 -0
- data/spec/opentok/broadcasts_spec.rb +157 -1
- data/spec/opentok/connection_spec.rb +1 -1
- data/spec/opentok/opentok_spec.rb +6 -0
- data/spec/opentok/renders_spec.rb +142 -0
- data/spec/opentok/sip_spec.rb +32 -1
- data/spec/opentok/streams_spec.rb +21 -1
- metadata +75 -7
- data/.travis.yml +0 -17
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,122 @@ 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
|
+
|
557
|
+
def start_render(session_id, opts)
|
558
|
+
opts.extend(HashExtensions)
|
559
|
+
body = { :sessionId => session_id }.merge(opts.camelize_keys!)
|
560
|
+
response = self.class.post("/v2/project/#{@api_key}/render", {
|
561
|
+
:body => body.to_json,
|
562
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
563
|
+
})
|
564
|
+
case response.code
|
565
|
+
when 202
|
566
|
+
response
|
567
|
+
when 400
|
568
|
+
raise OpenTokRenderError, "The render could not be started. The request was invalid."
|
569
|
+
when 403
|
570
|
+
raise OpenTokAuthenticationError, "Authentication failed while starting a render. API Key: #{@api_key}"
|
571
|
+
when 500
|
572
|
+
raise OpenTokError, "OpenTok server error."
|
573
|
+
else
|
574
|
+
raise OpenTokRenderError, "The render could not be started"
|
575
|
+
end
|
576
|
+
rescue StandardError => e
|
577
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
578
|
+
end
|
579
|
+
|
580
|
+
def get_render(render_id)
|
581
|
+
response = self.class.get("/v2/project/#{@api_key}/render/#{render_id}", {
|
582
|
+
:headers => generate_headers
|
583
|
+
})
|
584
|
+
case response.code
|
585
|
+
when 200
|
586
|
+
response
|
587
|
+
when 400
|
588
|
+
raise OpenTokRenderError, "The request was invalid."
|
589
|
+
when 403
|
590
|
+
raise OpenTokAuthenticationError, "Authentication failed while getting a render. API Key: #{@api_key}"
|
591
|
+
when 404
|
592
|
+
raise OpenTokRenderError, "No matching render found (with the specified ID)"
|
593
|
+
when 500
|
594
|
+
raise OpenTokError, "OpenTok server error."
|
595
|
+
else
|
596
|
+
raise OpenTokRenderError, "Could not fetch render information."
|
597
|
+
end
|
598
|
+
rescue StandardError => e
|
599
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
600
|
+
end
|
601
|
+
|
602
|
+
def stop_render(render_id)
|
603
|
+
response = self.class.delete("/v2/project/#{@api_key}/render/#{render_id}", {
|
604
|
+
:headers => generate_headers
|
605
|
+
})
|
606
|
+
case response.code
|
607
|
+
when 204
|
608
|
+
response
|
609
|
+
when 400
|
610
|
+
raise OpenTokRenderError, "The request was invalid."
|
611
|
+
when 403
|
612
|
+
raise OpenTokAuthenticationError, "Authentication failed while stopping a render. API Key: #{@api_key}"
|
613
|
+
when 404
|
614
|
+
raise OpenTokRenderError, "No matching render found (with the specified ID) or it is already stopped"
|
615
|
+
when 500
|
616
|
+
raise OpenTokError, "OpenTok server error."
|
617
|
+
else
|
618
|
+
raise OpenTokRenderError, "The render could not be stopped."
|
619
|
+
end
|
620
|
+
rescue StandardError => e
|
621
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
622
|
+
end
|
623
|
+
|
624
|
+
def list_renders(offset, count)
|
625
|
+
query = Hash.new
|
626
|
+
query[:offset] = offset unless offset.nil?
|
627
|
+
query[:count] = count unless count.nil?
|
628
|
+
response = self.class.get("/v2/project/#{@api_key}/render", {
|
629
|
+
:query => query.empty? ? nil : query,
|
630
|
+
:headers => generate_headers,
|
631
|
+
})
|
632
|
+
case response.code
|
633
|
+
when 200
|
634
|
+
response
|
635
|
+
when 403
|
636
|
+
raise OpenTokAuthenticationError,
|
637
|
+
"Authentication failed while retrieving renders. API Key: #{@api_key}"
|
638
|
+
when 500
|
639
|
+
raise OpenTokError, "OpenTok server error."
|
640
|
+
else
|
641
|
+
raise OpenTokRenderError, "The renders could not be retrieved."
|
642
|
+
end
|
643
|
+
rescue StandardError => e
|
644
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
645
|
+
end
|
646
|
+
|
396
647
|
end
|
397
648
|
end
|
data/lib/opentok/connections.rb
CHANGED
data/lib/opentok/constants.rb
CHANGED
data/lib/opentok/exceptions.rb
CHANGED
@@ -14,5 +14,7 @@ module OpenTok
|
|
14
14
|
class OpenTokStreamLayoutError < OpenTokError; end
|
15
15
|
# Defines errors raised when you perform Broadcast operations.
|
16
16
|
class OpenTokBroadcastError < OpenTokError; end
|
17
|
-
|
17
|
+
# Defines errors raised when you perform Experience Composer render operations.
|
18
|
+
class OpenTokRenderError < OpenTokError; end
|
19
|
+
|
18
20
|
end
|
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"
|
@@ -8,9 +11,7 @@ require "opentok/sip"
|
|
8
11
|
require "opentok/streams"
|
9
12
|
require "opentok/signals"
|
10
13
|
require "opentok/broadcasts"
|
11
|
-
|
12
|
-
require "resolv"
|
13
|
-
require "set"
|
14
|
+
require "opentok/renders"
|
14
15
|
|
15
16
|
module OpenTok
|
16
17
|
# Contains methods for creating OpenTok sessions and generating tokens. It also includes
|
@@ -42,9 +43,9 @@ module OpenTok
|
|
42
43
|
# streams, and signal. (This is the default value if you do not specify a role.)
|
43
44
|
#
|
44
45
|
# * <code>:moderator</code> -- In addition to the privileges granted to a
|
45
|
-
# publisher,
|
46
|
-
#
|
47
|
-
#
|
46
|
+
# publisher, a moderator can perform moderation functions, such as forcing clients
|
47
|
+
# to disconnect, to stop publishing streams, or to mute audio in published streams. See the
|
48
|
+
# {https://tokbox.com/developer/guides/moderation/ Moderation developer guide}.
|
48
49
|
# @option options [integer] :expire_time The expiration time, in seconds since the UNIX epoch.
|
49
50
|
# Pass in 0 to use the default expiration time of 24 hours after the token creation time.
|
50
51
|
# The maximum expiration time is 30 days after the creation time.
|
@@ -77,8 +78,8 @@ module OpenTok
|
|
77
78
|
# @param [String] api_key The OpenTok API key for your
|
78
79
|
# {https://tokbox.com/account OpenTok project}.
|
79
80
|
# @param [String] api_secret Your OpenTok API key.
|
80
|
-
# @option opts [Symbol] :api_url Do not set this parameter. It is for internal use by
|
81
|
-
# @option opts [Symbol] :ua_addendum Do not set this parameter. It is for internal use by
|
81
|
+
# @option opts [Symbol] :api_url Do not set this parameter. It is for internal use by Vonage.
|
82
|
+
# @option opts [Symbol] :ua_addendum Do not set this parameter. It is for internal use by Vonage.
|
82
83
|
# @option opts [Symbol] :timeout_length Custom timeout in seconds. If not provided, defaults to 2 seconds.
|
83
84
|
def initialize(api_key, api_secret, opts={})
|
84
85
|
@api_key = api_key.to_s()
|
@@ -192,6 +193,11 @@ module OpenTok
|
|
192
193
|
@broadcasts ||= Broadcasts.new client
|
193
194
|
end
|
194
195
|
|
196
|
+
# A Renders object, which lets you work with OpenTok Experience Composer renders.
|
197
|
+
def renders
|
198
|
+
@renders ||= Renders.new client
|
199
|
+
end
|
200
|
+
|
195
201
|
# A Sip object, which lets you use the OpenTok SIP gateway.
|
196
202
|
def sip
|
197
203
|
@sip ||= Sip.new client
|
@@ -207,14 +213,14 @@ module OpenTok
|
|
207
213
|
@signals ||= Signals.new client
|
208
214
|
end
|
209
215
|
|
210
|
-
# A Connections object, which lets disconnect clients from an OpenTok session.
|
216
|
+
# A Connections object, which lets you disconnect clients from an OpenTok session.
|
211
217
|
def connections
|
212
218
|
@connections ||= Connections.new client
|
213
219
|
end
|
214
220
|
|
215
221
|
protected
|
216
222
|
def client
|
217
|
-
@client ||= Client.new api_key, api_secret, api_url, ua_addendum
|
223
|
+
@client ||= Client.new api_key, api_secret, api_url, ua_addendum, timeout_length: @timeout_length
|
218
224
|
end
|
219
225
|
|
220
226
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "active_support/inflector"
|
2
|
+
|
3
|
+
module OpenTok
|
4
|
+
# Represents an Experience Composer render of an OpenTok session.
|
5
|
+
# See {https://tokbox.com/developer/guides/experience-composer/ Experience Composer}.
|
6
|
+
#
|
7
|
+
# @attr [string] id
|
8
|
+
# The unique ID for the Experience Composer.
|
9
|
+
#
|
10
|
+
# @attr [string] session_id
|
11
|
+
# The session ID of the OpenTok session associated with this render.
|
12
|
+
#
|
13
|
+
# @attr [string] project_id
|
14
|
+
# The API key associated with the render.
|
15
|
+
#
|
16
|
+
# @attr [int] created_at
|
17
|
+
# The time the Experience Composer started, expressed in milliseconds since the Unix epoch.
|
18
|
+
#
|
19
|
+
# @attr [int] updated_at
|
20
|
+
# The UNIX timestamp when the Experience Composer status was last updated.
|
21
|
+
#
|
22
|
+
# @attr [string] url
|
23
|
+
# A publicly reachable URL controlled by the customer and capable of generating the content to be rendered without user intervention.
|
24
|
+
#
|
25
|
+
# @attr [string] resolution
|
26
|
+
# The resolution of the Experience Composer (either "640x480", "480x640", "1280x720", "720x1280", "1920x1080", or "1080x1920").
|
27
|
+
#
|
28
|
+
# @attr [string] status
|
29
|
+
# The status of the Experience Composer. Poll frequently to check status updates. This property set to one of the following:
|
30
|
+
# - "starting" — The Vonage Video API platform is in the process of connecting to the remote application at the URL provided. This is the initial state.
|
31
|
+
# - "started" — The Vonage Video API platform has successfully connected to the remote application server, and is publishing the web view to an OpenTok stream.
|
32
|
+
# - "stopped" — The Experience Composer has stopped.
|
33
|
+
# - "failed" — An error occurred and the Experience Composer could not proceed. It may occur at startup if the OpenTok server cannot connect to the remote
|
34
|
+
# application server or republish the stream. It may also occur at any point during the process due to an error in the Vonage Video API platform.
|
35
|
+
#
|
36
|
+
# @attr [string] reason
|
37
|
+
# The reason field is only available when the status is either "stopped" or "failed". If the status is stopped, the reason field will contain either
|
38
|
+
# "Max Duration Exceeded" or "Stop Requested." If the status is failed, the reason will contain a more specific error message.
|
39
|
+
#
|
40
|
+
# @attr [string] streamId
|
41
|
+
# The ID of the composed stream being published. The streamId is not available when the status is "starting" and may not be available when the status is "failed".
|
42
|
+
class Render
|
43
|
+
|
44
|
+
# @private
|
45
|
+
def initialize(interface, json)
|
46
|
+
@interface = interface
|
47
|
+
# TODO: validate json fits schema
|
48
|
+
@json = json
|
49
|
+
end
|
50
|
+
|
51
|
+
# A JSON-encoded string representation of the Experience Composer render.
|
52
|
+
def to_json
|
53
|
+
@json.to_json
|
54
|
+
end
|
55
|
+
|
56
|
+
# Stops the OpenTok Experience Composer render.
|
57
|
+
def stop
|
58
|
+
# TODO: validate returned json fits schema
|
59
|
+
@json = @interface.stop @json['id']
|
60
|
+
end
|
61
|
+
|
62
|
+
# Gets info about the OpenTok Experience Composer render.
|
63
|
+
def info
|
64
|
+
# TODO: validate returned json fits schema
|
65
|
+
@json = @interface.find @json['id']
|
66
|
+
end
|
67
|
+
|
68
|
+
# @private ignore
|
69
|
+
def method_missing(method, *args, &block)
|
70
|
+
camelized_method = method.to_s.camelize(:lower)
|
71
|
+
if @json.has_key? camelized_method and args.empty?
|
72
|
+
@json[camelized_method]
|
73
|
+
else
|
74
|
+
super method, *args, &block
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "opentok/render"
|
2
|
+
|
3
|
+
module OpenTok
|
4
|
+
# A class for accessing an array of Experience Composer Render objects.
|
5
|
+
class RenderList < Array
|
6
|
+
# The total number of Experience Composer renders.
|
7
|
+
attr_reader :total
|
8
|
+
|
9
|
+
def initialize(interface, json)
|
10
|
+
@total = json["count"]
|
11
|
+
super json["items"].map { |item| ::OpenTok::Render.new interface, item }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "opentok/client"
|
2
|
+
require "opentok/render"
|
3
|
+
require "opentok/render_list"
|
4
|
+
|
5
|
+
module OpenTok
|
6
|
+
# A class for working with OpenTok Experience Composer renders.
|
7
|
+
# See {https://tokbox.com/developer/guides/experience-composer/ Experience Composer}.
|
8
|
+
class Renders
|
9
|
+
|
10
|
+
# @private
|
11
|
+
def initialize(client)
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
# Starts an Experience Composer render for an OpenTok session.
|
16
|
+
#
|
17
|
+
# @param [String] session_id (Required) The session ID of the OpenTok session that will include the Experience Composer stream.
|
18
|
+
#
|
19
|
+
# @param [Hash] options (Required) A hash defining options for the render.
|
20
|
+
#
|
21
|
+
# @option options [String] :token (Required) A valid OpenTok token with a Publisher role and (optionally) connection data to be associated with the output stream.
|
22
|
+
#
|
23
|
+
# @option options [String] :url (Required) A publicly reachable URL controlled by the customer and capable of generating the content to be rendered without user intervention.
|
24
|
+
# The minimum length of the URL is 15 characters and the maximum length is 2048 characters.
|
25
|
+
#
|
26
|
+
# @option options [Integer] :max_duration (Optional) The maximum time allowed for the Experience Composer, in seconds. After this time, it is stopped
|
27
|
+
# automatically, if it is still running. The maximum value is 36000 (10 hours), the minimum value is 60 (1 minute), and the default value is 7200 (2 hours).
|
28
|
+
# When the Experience Composer ends, its stream is unpublished and an event is posted to the callback URL, if configured in the Account Portal.
|
29
|
+
#
|
30
|
+
# @option options [String] :resolution The resolution of the Experience Composer, either "640x480" (SD landscape), "480x640" (SD portrait), "1280x720" (HD landscape),
|
31
|
+
# "720x1280" (HD portrait), "1920x1080" (FHD landscape), or "1080x1920" (FHD portrait). By default, this resolution is "1280x720" (HD landscape, the default).
|
32
|
+
#
|
33
|
+
# @option options [Hash] :properties (Optional) The initial configuration of Publisher properties for the composed output stream. The properties object contains
|
34
|
+
# the key <code>:name</code> (String) which serves as the name of the composed output stream which is published to the session. The name must have a minimum length of 1 and
|
35
|
+
# a maximum length of 200.
|
36
|
+
#
|
37
|
+
# @return [Render] The render object, which includes properties defining the render, including the render ID.
|
38
|
+
#
|
39
|
+
# @raise [OpenTokRenderError] The render could not be started. The request was invalid.
|
40
|
+
# @raise [OpenTokAuthenticationError] Authentication failed while starting a render. Invalid API key.
|
41
|
+
# @raise [OpenTokError] OpenTok server error.
|
42
|
+
def start(session_id, options = {})
|
43
|
+
raise ArgumentError, "session_id not provided" if session_id.to_s.empty?
|
44
|
+
raise ArgumentError, "options cannot be empty" if options.empty?
|
45
|
+
raise ArgumentError, "token property is required in options" unless options.has_key?(:token)
|
46
|
+
raise ArgumentError, "url property is required in options" unless options.has_key?(:url)
|
47
|
+
|
48
|
+
render_json = @client.start_render(session_id, options)
|
49
|
+
Render.new self, render_json
|
50
|
+
end
|
51
|
+
|
52
|
+
# Stops an OpenTok Experience Composer render.
|
53
|
+
#
|
54
|
+
# @param [String] render_id (Required) The render ID.
|
55
|
+
#
|
56
|
+
# @return [Render] The render object, which includes properties defining the render.
|
57
|
+
#
|
58
|
+
# @raise [OpenTokRenderError] The request was invalid.
|
59
|
+
# @raise [OpenTokAuthenticationError] Authentication failed while stopping a render. Invalid API key.
|
60
|
+
# @raise [OpenTokRenderError] No matching render found (with the specified ID) or it is already stopped
|
61
|
+
# @raise [OpenTokError] OpenTok server error.
|
62
|
+
def stop(render_id)
|
63
|
+
raise ArgumentError, "render_id not provided" if render_id.to_s.empty?
|
64
|
+
|
65
|
+
render_json = @client.stop_render(render_id)
|
66
|
+
Render.new self, render_json
|
67
|
+
end
|
68
|
+
|
69
|
+
# Gets a Render object for the given render ID.
|
70
|
+
#
|
71
|
+
# @param [String] render_id (Required) The render ID.
|
72
|
+
#
|
73
|
+
# @return [Render] The render object, which includes properties defining the render.
|
74
|
+
#
|
75
|
+
# @raise [OpenTokRenderError] The request was invalid.
|
76
|
+
# @raise [OpenTokAuthenticationError] Authentication failed while stopping a render. Invalid API key.
|
77
|
+
# @raise [OpenTokRenderError] No matching render found (with the specified ID) or it is already stopped
|
78
|
+
# @raise [OpenTokError] OpenTok server error.
|
79
|
+
def find(render_id)
|
80
|
+
raise ArgumentError, "render_id not provided" if render_id.to_s.empty?
|
81
|
+
|
82
|
+
render_json = @client.get_render(render_id.to_s)
|
83
|
+
Render.new self, render_json
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns a RenderList, which is an array of Experience Composers renders that are associated with a project.
|
87
|
+
#
|
88
|
+
# @param [Hash] options A hash with keys defining which range of renders to retrieve.
|
89
|
+
# @option options [integer] :offset (Optional). The start offset for the list of Experience Composer renders. The default is 0.
|
90
|
+
# @option options [integer] :count Optional. The number of Experience Composer renders to retrieve starting at the offset. The default value
|
91
|
+
# is 50 and the maximum is 1000.
|
92
|
+
#
|
93
|
+
# @return [RenderList] An RenderList object, which is an array of Render objects.
|
94
|
+
def list(options = {})
|
95
|
+
raise ArgumentError, "Limit is invalid" unless options[:count].nil? || (0..1000).include?(options[:count])
|
96
|
+
|
97
|
+
render_list_json = @client.list_renders(options[:offset], options[:count])
|
98
|
+
RenderList.new self, render_list_json
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/opentok/session.rb
CHANGED
@@ -31,10 +31,10 @@ module OpenTok
|
|
31
31
|
# * <code>:publisher</code> -- A publisher can publish streams, subscribe to
|
32
32
|
# streams, and signal. (This is the default value if you do not specify a role.)
|
33
33
|
#
|
34
|
-
# * <code>:moderator</code> --
|
35
|
-
# publisher,
|
36
|
-
#
|
37
|
-
#
|
34
|
+
# * <code>:moderator</code> -- n addition to the privileges granted to a
|
35
|
+
# publisher, a moderator can perform moderation functions, such as forcing clients
|
36
|
+
# to disconnect, to stop publishing streams, or to mute audio in published streams. See the
|
37
|
+
# {https://tokbox.com/developer/guides/moderation/ Moderation developer guide}.
|
38
38
|
# @option options [integer] :expire_time The expiration time, in seconds since the UNIX epoch.
|
39
39
|
# Pass in 0 to use the default expiration time of 24 hours after the token creation time.
|
40
40
|
# The maximum expiration time is 30 days after the creation time.
|
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
|