opentok 4.1.0 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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