opentok 4.1.2 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +30 -0
  3. data/CHANGES.md +157 -0
  4. data/README.md +4 -2
  5. data/lib/opentok/archive.rb +50 -3
  6. data/lib/opentok/archives.rb +102 -6
  7. data/lib/opentok/broadcast.rb +44 -1
  8. data/lib/opentok/broadcast_list.rb +14 -0
  9. data/lib/opentok/broadcasts.rb +168 -17
  10. data/lib/opentok/client.rb +161 -0
  11. data/lib/opentok/connections.rb +1 -1
  12. data/lib/opentok/constants.rb +1 -0
  13. data/lib/opentok/opentok.rb +9 -9
  14. data/lib/opentok/sip.rb +40 -2
  15. data/lib/opentok/streams.rb +48 -1
  16. data/lib/opentok/token_generator.rb +1 -0
  17. data/lib/opentok/version.rb +1 -1
  18. data/opentok.gemspec +1 -1
  19. data/sample/Broadcast/README.md +42 -0
  20. data/sample/Broadcast/broadcast_sample.rb +15 -0
  21. data/sample/Broadcast/views/all.erb +46 -0
  22. data/sample/Broadcast/views/index.erb +16 -1
  23. data/spec/cassettes/OpenTok_Archives/adds_a_stream_to_an_archive.yml +37 -0
  24. data/spec/cassettes/OpenTok_Archives/removes_a_stream_from_an_archive.yml +37 -0
  25. data/spec/cassettes/OpenTok_Archives/should_create_layout_archives_with_screenshare_layout_types.yml +50 -0
  26. data/spec/cassettes/OpenTok_Broadcasts/adds_a_stream_to_a_broadcast.yml +37 -0
  27. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_all_broadcasts.yml +192 -0
  28. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_broadcasts_with_an_offset.yml +117 -0
  29. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_count_number_of_broadcasts.yml +92 -0
  30. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_part_of_the_broadcasts_when_using_offset_and_count.yml +142 -0
  31. data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_session_broadcasts.yml +117 -0
  32. data/spec/cassettes/OpenTok_Broadcasts/removes_a_stream_from_a_broadcast.yml +37 -0
  33. data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_connection/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
  34. data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_session/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
  35. data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +2 -2
  36. data/spec/cassettes/OpenTok_Streams/disables_the_mute_state_of_a_session.yml +37 -0
  37. data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted.yml +39 -0
  38. data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted_except_specified_excluded_streams.yml +39 -0
  39. data/spec/cassettes/OpenTok_Streams/forces_the_specified_stream_to_be_muted.yml +39 -0
  40. data/spec/opentok/archives_spec.rb +40 -0
  41. data/spec/opentok/broadcasts_spec.rb +96 -2
  42. data/spec/opentok/connection_spec.rb +1 -1
  43. data/spec/opentok/sip_spec.rb +32 -1
  44. data/spec/opentok/streams_spec.rb +21 -1
  45. metadata +41 -6
  46. data/.travis.yml +0 -17
@@ -1,6 +1,6 @@
1
1
  require "opentok/client"
2
2
  require "opentok/broadcast"
3
-
3
+ require "opentok/broadcast_list"
4
4
 
5
5
  module OpenTok
6
6
  # A class for working with OpenTok live streaming broadcasts.
@@ -22,42 +22,88 @@ module OpenTok
22
22
  # @param [String] session_id The session ID of the OpenTok session to broadcast.
23
23
  #
24
24
  # @param [Hash] options A hash defining options for the broadcast.
25
- # @option options [Hash] :layout Specify this to assign the initial layout for the broadcast.
26
- # Valid values for the layout (<code>:type</code>) property are "bestFit" (best fit), "custom" (custom),
27
- # "horizontalPresentation" (horizontal presentation), "pip" (picture-in-picture), and
28
- # "verticalPresentation" (vertical presentation)).
29
- # If you specify a (<code>:custom</code>) layout type, set the (<code>:stylesheet</code>) property of the layout object
30
- # to the stylesheet. (For other layout types, do not set a stylesheet property.)
31
- # If you do not specify an initial layout type, the broadcast stream uses the Best Fit layout type.
25
+ #
26
+ # @option options [Hash] :layout Specify this to assign the initial layout type for
27
+ # the broadcast. This is a hash containing three keys:
28
+ # <code>:type</code>, <code>:stylesheet<code> and <code>:screenshare_type</code>.
29
+ # Valid values for <code>:type</code> are "bestFit" (best fit), "custom" (custom),
30
+ # "horizontalPresentation" (horizontal presentation),
31
+ # "pip" (picture-in-picture), and "verticalPresentation" (vertical presentation)).
32
+ # If you specify a "custom" layout type, set the <code>:stylesheet</code> key to the
33
+ # stylesheet (CSS). (For other layout types, do not set the <code>:stylesheet</code> key.)
34
+ # Valid values for <code>:screenshare_type</code> are "bestFit", "pip",
35
+ # "verticalPresentation", "horizontalPresentation". This property is optional.
36
+ # If it is specified, then the <code>:type</code> property **must** be set to "bestFit".
37
+ # If you do not specify an initial layout type, the broadcast uses the best fit
38
+ # layout type.
32
39
  #
33
40
  # @option options [int] maxDuration
34
41
  # The maximum duration for the broadcast, in seconds. The broadcast will automatically stop when
35
42
  # the maximum duration is reached. You can set the maximum duration to a value from 60 (60 seconds) to 36000 (10 hours).
36
- # The default maximum duration is 2 hours (7200 seconds).
43
+ # The default maximum duration is 4 hours (14,400 seconds).
37
44
  #
38
- # @option options [Hash] outputs
45
+ # @option options [Hash] outputs (Required)
39
46
  # This object defines the types of broadcast streams you want to start (both HLS and RTMP).
40
47
  # You can include HLS, RTMP, or both as broadcast streams. If you include RTMP streaming,
41
48
  # you can specify up to five target RTMP streams (or just one).
42
- # The (<code>:hls</code>) property is set to an empty [Hash] object. The HLS URL is returned in the response.
43
- # The (<code>:rtmp</code>) property is set to an [Array] of Rtmp [Hash] properties.
44
- # For each RTMP , specify (<code>:serverUrl</code>) for the RTMP server URL,
49
+ #
50
+ # For multiple RTMP streams, the (<code>:rtmp</code>) property is set to an [Array] of Rtmp [Hash] objects.
51
+ # For each RTMP hash, specify (<code>:serverUrl</code>) for the RTMP server URL,
45
52
  # (<code>:streamName</code>) such as the YouTube Live stream name or the Facebook stream key),
46
- # and (optionally) (<code>:id</code>), a unique ID for the stream.
53
+ # and (optionally) (<code>:id</code>), a unique ID for the stream. If you specify an ID, it will be
54
+ # included in the (<code>broadcast_json</code>) response from the Client#start_broadcast method call,
55
+ # and is also available in the (<code>broadcast_json</code>) response from the Broadcasts#find method.
56
+ # Vonage streams the session to each RTMP URL you specify. Note that OpenTok live streaming
57
+ # supports RTMP and RTMPS.
58
+ # If you need to support only one RTMP URL, you can set a Rtmp [Hash] object (instead of an array of
59
+ # objects) for the (<code>:rtmp</code>) property value in the (<code>:outputs</code>) [Hash].
60
+ #
61
+ # For HLS, the (<code>:hls</code>) property in the (<code>:outputs</code>) [Hash] is set to a HLS [Hash]
62
+ # object. This object includes the following optional properties:
63
+ # - (<code>:dvr</code>) (Boolean). Whether to enable
64
+ # {https://tokbox.com/developer/guides/broadcast/live-streaming/#dvr DVR functionality}
65
+ # (rewinding, pausing, and resuming)
66
+ # in players that support it (true), or not (false, the default). With DVR enabled, the HLS URL will
67
+ # include a ?DVR query string appended to the end.
68
+ # - (<code>:low_latency</code>) (Boolean). Whether to enable
69
+ # {https://tokbox.com/developer/guides/broadcast/live-streaming/#low-latency low-latency mode}
70
+ # for the HLSstream.
71
+ # Some HLS players do not support low-latency mode. This feature is incompatible with DVR mode HLS
72
+ # broadcasts (both can't be set to true). This is a beta feature.
73
+ # The HLS URL is included in the (<code>broadcast_json</code>) response from the Client#start_broadcast
74
+ # method call, and is also available in the (<code>broadcast_json</code>) response from the
75
+ # Broadcasts#find method.
47
76
  #
48
77
  # @option options [string] resolution
49
78
  # The resolution of the broadcast: either "640x480" (SD, the default) or "1280x720" (HD).
50
79
  #
80
+ # @option options [String] :streamMode (Optional) Whether streams included in the broadcast are selected
81
+ # automatically ("auto", the default) or manually ("manual"). When streams are selected automatically ("auto"),
82
+ # all streams in the session can be included in the broadcast. When streams are selected manually ("manual"),
83
+ # you specify streams to be included based on calls to the
84
+ # {Broadcasts#add_stream} method. You can specify whether a
85
+ # stream's audio, video, or both are included in the broadcast.
86
+ # For both automatic and manual modes, the broadcast composer includes streams based
87
+ # on {https://tokbox.com/developer/guides/archive-broadcast-layout/#stream-prioritization-rules stream prioritization rules}.
88
+ # Important: this feature is currently available in the Standard environment only.
89
+ #
51
90
  # @return [Broadcast] The broadcast object, which includes properties defining the broadcast,
52
91
  # including the broadcast ID.
53
92
  #
54
93
  # @raise [OpenTokBroadcastError] The broadcast could not be started. The request was invalid or broadcast already started
55
- # @raise [OpenTokAuthenticationError] Authentication failed while starting an archive.
94
+ # @raise [OpenTokAuthenticationError] Authentication failed while starting an broadcast.
56
95
  # Invalid API key.
57
96
  # @raise [OpenTokError] OpenTok server error.
58
97
  def create(session_id, options = {})
59
98
  raise ArgumentError, "session_id not provided" if session_id.to_s.empty?
60
99
  raise ArgumentError, "options cannot be empty" if options.empty?
100
+ raise ArgumentError, "outputs property is required in options" unless options.has_key?(:outputs)
101
+ raise ArgumentError, "outputs must be a Hash" unless options[:outputs].is_a? Hash
102
+ if options[:outputs].has_key?(:hls)
103
+ dvr = options[:outputs][:hls][:dvr]
104
+ low_latency = options[:outputs][:hls][:low_latency]
105
+ raise ArgumentError, "dvr and low_latency can't both be true for HLS" if hls_dvr_and_low_latency_options_both_true?(dvr, low_latency)
106
+ end
61
107
  broadcast_json = @client.start_broadcast(session_id, options)
62
108
  Broadcast.new self, broadcast_json
63
109
  end
@@ -78,6 +124,25 @@ module OpenTok
78
124
  Broadcast.new self, broadcast_json
79
125
  end
80
126
 
127
+ # Returns a BroadcastList, which is an array of broadcasts that are completed and in-progress,
128
+ # for your API key.
129
+ #
130
+ # @param [Hash] options A hash with keys defining which range of broadcasts to retrieve.
131
+ # @option options [integer] :offset Optional. The index offset of the first broadcast. 0 is offset
132
+ # of the most recently started broadcast. 1 is the offset of the broadcast that started prior to
133
+ # the most recent broadcast. If you do not specify an offset, 0 is used.
134
+ # @option options [integer] :count Optional. The number of broadcasts to be returned. The maximum
135
+ # number of broadcasts returned is 1000.
136
+ # @option options [String] :session_id Optional. The session ID that broadcasts belong to.
137
+ # https://tokbox.com/developer/rest/#list_broadcasts
138
+ #
139
+ # @return [BroadcastList] An BroadcastList object, which is an array of Broadcast objects.
140
+ def all(options = {})
141
+ raise ArgumentError, "Limit is invalid" unless options[:count].nil? || (0..1000).include?(options[:count])
142
+
143
+ broadcast_list_json = @client.list_broadcasts(options[:offset], options[:count], options[:sessionId])
144
+ BroadcastList.new self, broadcast_list_json
145
+ end
81
146
 
82
147
  # Stops an OpenTok broadcast
83
148
  #
@@ -104,7 +169,7 @@ module OpenTok
104
169
  # @param [String] broadcast_id
105
170
  # The broadcast ID.
106
171
  #
107
- # @option options [String] :type
172
+ # @option options [String] :type
108
173
  # The layout type. Set this to "bestFit", "pip", "verticalPresentation",
109
174
  # "horizontalPresentation", "focus", or "custom".
110
175
  #
@@ -112,6 +177,11 @@ module OpenTok
112
177
  # The stylesheet for a custom layout. Set this parameter
113
178
  # if you set <code>type</code> to <code>"custom"</code>. Otherwise, leave it undefined.
114
179
  #
180
+ # @option options [String] :screenshare_type
181
+ # The screenshare layout type. Set this to "bestFit", "pip", "verticalPresentation" or
182
+ # "horizonalPresentation". If this is defined, then the <code>type</code> parameter
183
+ # must be set to <code>"bestFit"</code>.
184
+ #
115
185
  # @raise [OpenTokBroadcastError]
116
186
  # The broadcast layout could not be updated.
117
187
  #
@@ -137,13 +207,94 @@ module OpenTok
137
207
  raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty?
138
208
  type = options[:type]
139
209
  raise ArgumentError, "custom type must have a stylesheet" if (type.eql? "custom") && (!options.key? :stylesheet)
140
- valid_non_custom_type = ["bestFit","horizontalPresentation","pip", "verticalPresentation", ""].include? type
210
+ valid_non_custom_layouts = ["bestFit","horizontalPresentation","pip", "verticalPresentation", ""]
211
+ valid_non_custom_type = valid_non_custom_layouts.include? type
141
212
  raise ArgumentError, "type is not valid" if !valid_non_custom_type && !(type.eql? "custom")
142
213
  raise ArgumentError, "stylesheet not needed" if valid_non_custom_type and options.key? :stylesheet
214
+ raise ArgumentError, "screenshare_type is not valid" if options[:screenshare_type] && !valid_non_custom_layouts.include?(options[:screenshare_type])
215
+ raise ArgumentError, "type must be set to 'bestFit' if screenshare_type is defined" if options[:screenshare_type] && type != 'bestFit'
143
216
  response = @client.layout_broadcast(broadcast_id, options)
144
217
  (200..300).include? response.code
145
218
  end
146
219
 
220
+ # Adds a stream to currently running broadcast that was started with the
221
+ # streamMode set to "manual". For a description of the feature, see
222
+ # {https://tokbox.com/developer/rest/#selecting-broadcast-streams}.
223
+ #
224
+ # @param [String] broadcast_id
225
+ # The broadcast ID.
226
+ #
227
+ # @param [String] stream_id
228
+ # The ID for the stream to be added to the broadcast
229
+ #
230
+ # @option opts [true, false] :has_audio
231
+ # (Boolean, optional) — Whether the broadcast should include the stream's
232
+ # audio (true, the default) or not (false).
233
+ #
234
+ # @option opts [true, false] :has_video
235
+ # (Boolean, optional) — Whether the broadcast should include the stream's
236
+ # video (true, the default) or not (false).
237
+ #
238
+ # You can call the method repeatedly with add_stream set to the same stream ID, to
239
+ # toggle the stream's audio or video in the broadcast. If you set both has_audio and
240
+ # has_video to false, you will get error response.
241
+ #
242
+ # @raise [ArgumentError]
243
+ # The broadcast_id parameter is empty.
244
+ #
245
+ # @raise [ArgumentError]
246
+ # The stream_id parameter is empty.
247
+ #
248
+ # @raise [ArgumentError]
249
+ # The has_audio and has_video properties of the options parameter are both set to "false"
250
+ #
251
+ def add_stream(broadcast_id, stream_id, options)
252
+ raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty?
253
+ raise ArgumentError, "stream_id not provided" if stream_id.to_s.empty?
254
+ if options.has_key?(:has_audio) && options.has_key?(:has_video)
255
+ has_audio = options[:has_audio]
256
+ has_video = options[:has_video]
257
+ raise ArgumentError, "has_audio and has_video can't both be false" if audio_and_video_options_both_false?(has_audio, has_video)
258
+ end
259
+ options['add_stream'] = stream_id
260
+
261
+ @client.select_streams_for_broadcast(broadcast_id, options)
262
+ end
263
+
264
+ # Removes a stream from a currently running broadcast that was started with the
265
+ # streamMode set to "manual". For a description of the feature, see
266
+ # {https://tokbox.com/developer/rest/#selecting-broadcast-streams}.
267
+ #
268
+ # @param [String] broadcast_id
269
+ # The broadcast ID.
270
+ #
271
+ # @param [String] stream_id
272
+ # The ID for the stream to be removed from the broadcast
273
+ #
274
+ # @raise [ArgumentError]
275
+ # The broadcast_id parameter id is empty.
276
+ #
277
+ # @raise [ArgumentError]
278
+ # The stream_id parameter is empty.
279
+ #
280
+ def remove_stream(broadcast_id, stream_id)
281
+ raise ArgumentError, "broadcast_id not provided" if broadcast_id.to_s.empty?
282
+ raise ArgumentError, "stream_id not provided" if stream_id.to_s.empty?
283
+ options = {}
284
+ options['remove_stream'] = stream_id
285
+
286
+ @client.select_streams_for_broadcast(broadcast_id, options)
287
+ end
288
+
289
+ private
290
+
291
+ def audio_and_video_options_both_false?(has_audio, has_video)
292
+ has_audio == false && has_video == false
293
+ end
294
+
295
+ def hls_dvr_and_low_latency_options_both_true?(dvr, low_latency)
296
+ dvr == true && low_latency == true
297
+ end
147
298
 
148
299
  end
149
300
  end
@@ -188,6 +188,33 @@ module OpenTok
188
188
  raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
189
189
  end
190
190
 
191
+ def select_streams_for_archive(archive_id, opts)
192
+ opts.extend(HashExtensions)
193
+ body = opts.camelize_keys!
194
+ response = self.class.patch("/v2/project/#{@api_key}/archive/#{archive_id}/streams", {
195
+ :body => body.to_json,
196
+ :headers => generate_headers("Content-Type" => "application/json")
197
+ })
198
+ case response.code
199
+ when 204
200
+ response
201
+ when 400
202
+ raise OpenTokArchiveError, "The request was invalid."
203
+ when 403
204
+ raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
205
+ when 404
206
+ raise OpenTokArchiveError, "No matching archive found with the specified ID: #{archive_id}"
207
+ when 405
208
+ raise OpenTokArchiveError, "The archive was started with streamMode set to 'auto', which does not support stream manipulation."
209
+ when 500
210
+ raise OpenTokError, "OpenTok server error."
211
+ else
212
+ raise OpenTokArchiveError, "The archive streams could not be updated."
213
+ end
214
+ rescue StandardError => e
215
+ raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
216
+ end
217
+
191
218
  def forceDisconnect(session_id, connection_id)
192
219
  response = self.class.delete("/v2/project/#{@api_key}/session/#{session_id}/connection/#{connection_id}", {
193
220
  :headers => generate_headers("Content-Type" => "application/json")
@@ -206,6 +233,45 @@ module OpenTok
206
233
  raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
207
234
  end
208
235
 
236
+ def force_mute_stream(session_id, stream_id)
237
+ response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/stream/#{stream_id}/mute", {
238
+ :headers => generate_headers("Content-Type" => "application/json")
239
+ })
240
+ case response.code
241
+ when 200
242
+ response
243
+ when 400
244
+ raise ArgumentError, "Force mute failed. Stream ID #{stream_id} or Session ID #{session_id} is invalid"
245
+ when 403
246
+ raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
247
+ when 404
248
+ raise OpenTokConnectionError, "Either Stream ID #{stream_id} or Session ID #{session_id} is invalid"
249
+ end
250
+ rescue StandardError => e
251
+ raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
252
+ end
253
+
254
+ def force_mute_session(session_id, opts)
255
+ opts.extend(HashExtensions)
256
+ body = opts.camelize_keys!
257
+ response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/mute", {
258
+ :body => body.to_json,
259
+ :headers => generate_headers("Content-Type" => "application/json")
260
+ })
261
+ case response.code
262
+ when 200
263
+ response
264
+ when 400
265
+ raise ArgumentError, "Force mute failed. The request could not be processed due to a bad request"
266
+ when 403
267
+ raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
268
+ when 404
269
+ raise OpenTokConnectionError, "Session ID #{session_id} is invalid"
270
+ end
271
+ rescue StandardError => e
272
+ raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
273
+ end
274
+
209
275
  def signal(session_id, connection_id, opts)
210
276
  opts.extend(HashExtensions)
211
277
  connectionPath = connection_id.to_s.empty? ? "" : "/connection/#{connection_id}"
@@ -257,6 +323,52 @@ module OpenTok
257
323
  raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
258
324
  end
259
325
 
326
+ def play_dtmf_to_connection(session_id, connection_id, dtmf_digits)
327
+ body = { "digits" => dtmf_digits }
328
+
329
+ response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/connection/#{connection_id}/play-dtmf", {
330
+ :body => body.to_json,
331
+ :headers => generate_headers("Content-Type" => "application/json")
332
+ })
333
+ case response.code
334
+ when 200
335
+ response
336
+ when 400
337
+ raise ArgumentError, "One of the properties — dtmf_digits #{dtmf_digits} or session_id #{session_id} — is invalid."
338
+ when 403
339
+ raise OpenTokAuthenticationError, "Authentication failed. This can occur if you use an invalid OpenTok API key or an invalid JSON web token. API Key: #{@api_key}"
340
+ when 404
341
+ raise OpenTokError, "The specified session #{session_id} does not exist or the client specified by the #{connection_id} property is not connected to the session."
342
+ else
343
+ raise OpenTokError, "An error occurred when attempting to play DTMF digits to the session"
344
+ end
345
+ rescue StandardError => e
346
+ raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
347
+ end
348
+
349
+ def play_dtmf_to_session(session_id, dtmf_digits)
350
+ body = { "digits" => dtmf_digits }
351
+
352
+ response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/play-dtmf", {
353
+ :body => body.to_json,
354
+ :headers => generate_headers("Content-Type" => "application/json")
355
+ })
356
+ case response.code
357
+ when 200
358
+ response
359
+ when 400
360
+ raise ArgumentError, "One of the properties — dtmf_digits #{dtmf_digits} or session_id #{session_id} — is invalid."
361
+ when 403
362
+ raise OpenTokAuthenticationError, "Authentication failed. This can occur if you use an invalid OpenTok API key or an invalid JSON web token. API Key: #{@api_key}"
363
+ when 404
364
+ raise OpenTokError, "The specified session does not exist. Session ID: #{session_id}"
365
+ else
366
+ raise OpenTokError, "An error occurred when attempting to play DTMF digits to the session"
367
+ end
368
+ rescue StandardError => e
369
+ raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
370
+ end
371
+
260
372
  def info_stream(session_id, stream_id)
261
373
  streamId = stream_id.to_s.empty? ? '' : "/#{stream_id}"
262
374
  url = "/v2/project/#{@api_key}/session/#{session_id}/stream#{streamId}"
@@ -371,6 +483,28 @@ module OpenTok
371
483
  raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
372
484
  end
373
485
 
486
+ def list_broadcasts(offset, count, session_id)
487
+ query = Hash.new
488
+ query[:offset] = offset unless offset.nil?
489
+ query[:count] = count unless count.nil?
490
+ query[:sessionId] = session_id unless session_id.nil?
491
+ response = self.class.get("/v2/project/#{@api_key}/broadcast", {
492
+ :query => query.empty? ? nil : query,
493
+ :headers => generate_headers,
494
+ })
495
+ case response.code
496
+ when 200
497
+ response
498
+ when 403
499
+ raise OpenTokAuthenticationError,
500
+ "Authentication failed while retrieving broadcasts. API Key: #{@api_key}"
501
+ else
502
+ raise OpenTokBroadcastError, "The broadcasts could not be retrieved."
503
+ end
504
+ rescue StandardError => e
505
+ raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
506
+ end
507
+
374
508
  def layout_broadcast(broadcast_id, opts)
375
509
  opts.extend(HashExtensions)
376
510
  response = self.class.put("/v2/project/#{@api_key}/broadcast/#{broadcast_id}/layout", {
@@ -393,5 +527,32 @@ module OpenTok
393
527
  raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
394
528
  end
395
529
 
530
+ def select_streams_for_broadcast(broadcast_id, opts)
531
+ opts.extend(HashExtensions)
532
+ body = opts.camelize_keys!
533
+ response = self.class.patch("/v2/project/#{@api_key}/broadcast/#{broadcast_id}/streams", {
534
+ :body => body.to_json,
535
+ :headers => generate_headers("Content-Type" => "application/json")
536
+ })
537
+ case response.code
538
+ when 204
539
+ response
540
+ when 400
541
+ raise OpenTokBroadcastError, "The request was invalid."
542
+ when 403
543
+ raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
544
+ when 404
545
+ raise OpenTokBroadcastError, "No matching broadcast found with the specified ID: #{broadcast_id}"
546
+ when 405
547
+ raise OpenTokBroadcastError, "The broadcast was started with streamMode set to 'auto', which does not support stream manipulation."
548
+ when 500
549
+ raise OpenTokError, "OpenTok server error."
550
+ else
551
+ raise OpenTokBroadcastError, "The broadcast streams could not be updated."
552
+ end
553
+ rescue StandardError => e
554
+ raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
555
+ end
556
+
396
557
  end
397
558
  end
@@ -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" }
@@ -1,3 +1,6 @@
1
+ require "resolv"
2
+ require "set"
3
+
1
4
  require "opentok/constants"
2
5
  require "opentok/session"
3
6
  require "opentok/client"
@@ -9,9 +12,6 @@ require "opentok/streams"
9
12
  require "opentok/signals"
10
13
  require "opentok/broadcasts"
11
14
 
12
- require "resolv"
13
- require "set"
14
-
15
15
  module OpenTok
16
16
  # Contains methods for creating OpenTok sessions and generating tokens. It also includes
17
17
  # methods for returning object that let you work with archives, work with live streaming
@@ -42,9 +42,9 @@ module OpenTok
42
42
  # streams, and signal. (This is the default value if you do not specify a role.)
43
43
  #
44
44
  # * <code>:moderator</code> -- In addition to the privileges granted to a
45
- # 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.
45
+ # publisher, a moderator can perform moderation functions, such as forcing clients
46
+ # to disconnect, to stop publishing streams, or to mute audio in published streams. See the
47
+ # {https://tokbox.com/developer/guides/moderation/ Moderation developer guide}.
48
48
  # @option options [integer] :expire_time The expiration time, in seconds since the UNIX epoch.
49
49
  # Pass in 0 to use the default expiration time of 24 hours after the token creation time.
50
50
  # The maximum expiration time is 30 days after the creation time.
@@ -77,8 +77,8 @@ module OpenTok
77
77
  # @param [String] api_key The OpenTok API key for your
78
78
  # {https://tokbox.com/account OpenTok project}.
79
79
  # @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.
80
+ # @option opts [Symbol] :api_url Do not set this parameter. It is for internal use by Vonage.
81
+ # @option opts [Symbol] :ua_addendum Do not set this parameter. It is for internal use by Vonage.
82
82
  # @option opts [Symbol] :timeout_length Custom timeout in seconds. If not provided, defaults to 2 seconds.
83
83
  def initialize(api_key, api_secret, opts={})
84
84
  @api_key = api_key.to_s()
@@ -207,7 +207,7 @@ module OpenTok
207
207
  @signals ||= Signals.new client
208
208
  end
209
209
 
210
- # A Connections object, which lets disconnect clients from an OpenTok session.
210
+ # A Connections object, which lets you disconnect clients from an OpenTok session.
211
211
  def connections
212
212
  @connections ||= Connections.new client
213
213
  end
data/lib/opentok/sip.rb CHANGED
@@ -12,7 +12,9 @@ module OpenTok
12
12
  # "password" => sip_password },
13
13
  # "headers" => { "X-KEY1" => "value1",
14
14
  # "X-KEY1" => "value2" },
15
- # "secure" => "true"
15
+ # "secure" => "true",
16
+ # "video" => "true",
17
+ # "observe_force_mute" => "true"
16
18
  # }
17
19
  # response = opentok.sip.dial(session_id, token, "sip:+15128675309@acme.pstn.example.com;transport=tls", opts)
18
20
  # @param [String] session_id The session ID corresponding to the session to which
@@ -33,14 +35,50 @@ module OpenTok
33
35
  # @option opts [Hash] :auth This object contains the username and password
34
36
  # to be used in the the SIP INVITE​ request for HTTP digest authentication,
35
37
  # if it is required by your SIP platform.
36
- # @option opts [true, false] :secure 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
@@ -75,5 +75,52 @@ module OpenTok
75
75
  (200..300).include? response.code
76
76
  end
77
77
 
78
+ # Force a specific stream connected to an OpenTok session to mute itself.
79
+ #
80
+ # @param [String] session_id The session ID of the OpenTok session.
81
+ # @param [String] stream_id The stream ID of the stream in the session.
82
+ #
83
+ def force_mute(session_id, stream_id)
84
+ response = @client.force_mute_stream(session_id, stream_id)
85
+ end
86
+
87
+ # Force all streams connected to an OpenTok session (except for an optional list of streams),
88
+ # to mute published audio.
89
+ #
90
+ # In addition to existing streams, any streams that are published after the call to
91
+ # this method are published with audio muted. You can remove the mute state of a session
92
+ # by calling the disable_force_mute() method.
93
+ #
94
+ # @param [String] session_id The session ID.
95
+ # @param [Hash] opts An optional hash defining options for the muting action. For example:
96
+ # {
97
+ # "excluded_streams" => [
98
+ # "excludedStreamId1",
99
+ # "excludedStreamId2"
100
+ # ]
101
+ # }
102
+ # @option opts [Array] :excluded_streams The stream IDs for streams that should not be muted.
103
+ # This is an optional property. If you omit this property, all streams in the session will be muted.
104
+ #
105
+ def force_mute_all(session_id, opts = {})
106
+ opts['active'] = 'true'
107
+ response = @client.force_mute_session(session_id, opts)
108
+ end
109
+
110
+ # Disables the active mute state of the session. After you call this method, new streams
111
+ # published to the session will no longer have audio muted.
112
+ #
113
+ # After you call the force_mute_all() method, any streams published after
114
+ # the call are published with audio muted. Call the disable_force_mute() method
115
+ # to remove the mute state of a session, so that new published streams are not
116
+ # automatically muted.
117
+ #
118
+ # @param [String] session_id The session ID.
119
+ #
120
+ def disable_force_mute(session_id)
121
+ opts = {'active' => 'false'}
122
+ response = @client.force_mute_session(session_id, opts)
123
+ end
124
+
78
125
  end
79
- end
126
+ end
@@ -4,6 +4,7 @@ require "opentok/session"
4
4
  require "base64"
5
5
  require "addressable/uri"
6
6
  require "openssl"
7
+ require "active_support"
7
8
  require "active_support/time"
8
9
 
9
10
  module OpenTok
@@ -1,4 +1,4 @@
1
1
  module OpenTok
2
2
  # @private
3
- VERSION = '4.1.2'
3
+ VERSION = '4.4.0'
4
4
  end
data/opentok.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
 
19
19
  bundler_version = RUBY_VERSION < '2.1' ? '~> 1.5' : '>= 1.5'
20
20
  spec.add_development_dependency "bundler", bundler_version
21
- spec.add_development_dependency "rake", "~> 12.0.0"
21
+ spec.add_development_dependency "rake", "~> 12.3.3"
22
22
  spec.add_development_dependency "rspec", "~> 3.9.0"
23
23
  spec.add_development_dependency "webmock", ">= 2.3.2"
24
24
  spec.add_development_dependency "vcr", ">= 2.8.0"