opentok 4.1.0 → 4.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +30 -0
  3. data/.github/workflows/metrics.yml +17 -0
  4. data/CHANGES.md +171 -0
  5. data/README.md +62 -38
  6. data/lib/opentok/archive.rb +54 -4
  7. data/lib/opentok/archives.rb +115 -11
  8. data/lib/opentok/broadcast.rb +50 -3
  9. data/lib/opentok/broadcast_list.rb +14 -0
  10. data/lib/opentok/broadcasts.rb +178 -19
  11. data/lib/opentok/client.rb +251 -0
  12. data/lib/opentok/connections.rb +1 -1
  13. data/lib/opentok/constants.rb +1 -0
  14. data/lib/opentok/exceptions.rb +3 -1
  15. data/lib/opentok/opentok.rb +16 -10
  16. data/lib/opentok/render.rb +78 -0
  17. data/lib/opentok/render_list.rb +14 -0
  18. data/lib/opentok/renders.rb +101 -0
  19. data/lib/opentok/session.rb +4 -4
  20. data/lib/opentok/sip.rb +40 -2
  21. data/lib/opentok/streams.rb +49 -2
  22. data/lib/opentok/token_generator.rb +1 -0
  23. data/lib/opentok/version.rb +1 -1
  24. data/opentok.gemspec +2 -1
  25. data/sample/Broadcast/README.md +42 -0
  26. data/sample/Broadcast/broadcast_sample.rb +15 -0
  27. data/sample/Broadcast/views/all.erb +46 -0
  28. data/sample/Broadcast/views/index.erb +16 -1
  29. data/spec/cassettes/OpenTok_Archives/adds_a_stream_to_an_archive.yml +37 -0
  30. data/spec/cassettes/OpenTok_Archives/removes_a_stream_from_an_archive.yml +37 -0
  31. data/spec/cassettes/OpenTok_Archives/should_create_an_archive_with_matching_multi_archive_tag_when_multiArchiveTag_is_specified.yml +50 -0
  32. data/spec/cassettes/OpenTok_Archives/should_create_an_archive_with_multi_archive_tag_value_of_nil_when_multiArchiveTag_not_specified.yml +49 -0
  33. data/spec/cassettes/OpenTok_Archives/should_create_an_archives_with_a_specified_multiArchiveTag.yml +52 -0
  34. data/spec/cassettes/OpenTok_Archives/should_create_layout_archives_with_screenshare_layout_types.yml +50 -0
  35. data/spec/cassettes/OpenTok_Broadcasts/adds_a_stream_to_a_broadcast.yml +37 -0
  36. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_all_broadcasts.yml +192 -0
  37. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_broadcasts_with_an_offset.yml +117 -0
  38. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_count_number_of_broadcasts.yml +92 -0
  39. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_part_of_the_broadcasts_when_using_offset_and_count.yml +142 -0
  40. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_session_broadcasts.yml +117 -0
  41. data/spec/cassettes/OpenTok_Broadcasts/removes_a_stream_from_a_broadcast.yml +37 -0
  42. data/spec/cassettes/OpenTok_Broadcasts/starts_a_broadcast_with_a_matching_multi_broadcast_tag_value_when_multiBroadcastTag_is_specified.yml +54 -0
  43. data/spec/cassettes/OpenTok_Broadcasts/starts_a_broadcast_with_a_multi_broadcast_tag_value_of_nil_when_multiBroadcastTag_not_specified.yml +53 -0
  44. data/spec/cassettes/OpenTok_Broadcasts/starts_a_broadcast_with_a_specified_multiBroadcastTag.yml +54 -0
  45. data/spec/cassettes/OpenTok_Renders/finds_an_Experience_Composer_render.yml +46 -0
  46. data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_all_renders.yml +108 -0
  47. data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_count_number_of_renders.yml +63 -0
  48. data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_part_of_the_renders_when_using_offset_and_count.yml +63 -0
  49. data/spec/cassettes/OpenTok_Renders/for_many_renders/should_return_renders_with_an_offset.yml +74 -0
  50. data/spec/cassettes/OpenTok_Renders/starts_an_Experience_Composer_render.yml +48 -0
  51. data/spec/cassettes/OpenTok_Renders/stops_an_Experience_Composer_render.yml +28 -0
  52. data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_connection/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
  53. data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_session/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
  54. data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +2 -2
  55. data/spec/cassettes/OpenTok_Streams/disables_the_mute_state_of_a_session.yml +37 -0
  56. data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted.yml +39 -0
  57. data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted_except_specified_excluded_streams.yml +39 -0
  58. data/spec/cassettes/OpenTok_Streams/forces_the_specified_stream_to_be_muted.yml +39 -0
  59. data/spec/opentok/archives_spec.rb +61 -0
  60. data/spec/opentok/broadcasts_spec.rb +157 -1
  61. data/spec/opentok/connection_spec.rb +1 -1
  62. data/spec/opentok/opentok_spec.rb +6 -0
  63. data/spec/opentok/renders_spec.rb +142 -0
  64. data/spec/opentok/sip_spec.rb +32 -1
  65. data/spec/opentok/streams_spec.rb +21 -1
  66. metadata +75 -7
  67. data/.travis.yml +0 -17
@@ -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
@@ -25,4 +25,4 @@ module OpenTok
25
25
  end
26
26
 
27
27
  end
28
- end
28
+ end
@@ -1,4 +1,5 @@
1
1
  module OpenTok
2
+ require "set"
2
3
  API_URL = "https://api.opentok.com"
3
4
  TOKEN_SENTINEL = "T1=="
4
5
  ROLES = { subscriber: "subscriber", publisher: "publisher", moderator: "moderator" }
@@ -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
@@ -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, in clients using the OpenTok.js library, a moderator can call the
46
- # <code>forceUnpublish()</code> and <code>forceDisconnect()</code> method of the
47
- # Session object.
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 TokBox.
81
- # @option opts [Symbol] :ua_addendum Do not set this parameter. It is for internal use by TokBox.
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
@@ -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> -- In addition to the privileges granted to a
35
- # publisher, in clients using the OpenTok.js library, a moderator can call the
36
- # <code>forceUnpublish()</code> and <code>forceDisconnect()</code> method of the
37
- # Session object.
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 Wether the media must be transmitted
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