opentok 4.1.2 → 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/CHANGES.md +157 -0
- data/README.md +4 -2
- data/lib/opentok/archive.rb +50 -3
- data/lib/opentok/archives.rb +102 -6
- data/lib/opentok/broadcast.rb +44 -1
- data/lib/opentok/broadcast_list.rb +14 -0
- data/lib/opentok/broadcasts.rb +168 -17
- data/lib/opentok/client.rb +161 -0
- data/lib/opentok/connections.rb +1 -1
- data/lib/opentok/constants.rb +1 -0
- data/lib/opentok/opentok.rb +9 -9
- data/lib/opentok/sip.rb +40 -2
- data/lib/opentok/streams.rb +48 -1
- data/lib/opentok/token_generator.rb +1 -0
- data/lib/opentok/version.rb +1 -1
- data/opentok.gemspec +1 -1
- data/sample/Broadcast/README.md +42 -0
- data/sample/Broadcast/broadcast_sample.rb +15 -0
- data/sample/Broadcast/views/all.erb +46 -0
- data/sample/Broadcast/views/index.erb +16 -1
- data/spec/cassettes/OpenTok_Archives/adds_a_stream_to_an_archive.yml +37 -0
- data/spec/cassettes/OpenTok_Archives/removes_a_stream_from_an_archive.yml +37 -0
- data/spec/cassettes/OpenTok_Archives/should_create_layout_archives_with_screenshare_layout_types.yml +50 -0
- data/spec/cassettes/OpenTok_Broadcasts/adds_a_stream_to_a_broadcast.yml +37 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_all_broadcasts.yml +192 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_broadcasts_with_an_offset.yml +117 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_count_number_of_broadcasts.yml +92 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_part_of_the_broadcasts_when_using_offset_and_count.yml +142 -0
- data/spec/cassettes/OpenTok_Broadcasts/for_many_broadcasts/should_return_session_broadcasts.yml +117 -0
- data/spec/cassettes/OpenTok_Broadcasts/removes_a_stream_from_a_broadcast.yml +37 -0
- data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_connection/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
- data/spec/cassettes/OpenTok_Sip/_play_dtmf_to_session/returns_a_200_response_code_when_passed_a_valid_dtmf_digit_string.yml +39 -0
- data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +2 -2
- data/spec/cassettes/OpenTok_Streams/disables_the_mute_state_of_a_session.yml +37 -0
- data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted.yml +39 -0
- data/spec/cassettes/OpenTok_Streams/forces_all_current_and_future_streams_in_a_session_to_be_muted_except_specified_excluded_streams.yml +39 -0
- data/spec/cassettes/OpenTok_Streams/forces_the_specified_stream_to_be_muted.yml +39 -0
- data/spec/opentok/archives_spec.rb +40 -0
- data/spec/opentok/broadcasts_spec.rb +96 -2
- data/spec/opentok/connection_spec.rb +1 -1
- data/spec/opentok/sip_spec.rb +32 -1
- data/spec/opentok/streams_spec.rb +21 -1
- metadata +41 -6
- data/.travis.yml +0 -17
data/lib/opentok/broadcasts.rb
CHANGED
@@ -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
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
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
|
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
|
-
#
|
43
|
-
#
|
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
|
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
|
-
|
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
|
data/lib/opentok/client.rb
CHANGED
@@ -188,6 +188,33 @@ module OpenTok
|
|
188
188
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
189
189
|
end
|
190
190
|
|
191
|
+
def select_streams_for_archive(archive_id, opts)
|
192
|
+
opts.extend(HashExtensions)
|
193
|
+
body = opts.camelize_keys!
|
194
|
+
response = self.class.patch("/v2/project/#{@api_key}/archive/#{archive_id}/streams", {
|
195
|
+
:body => body.to_json,
|
196
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
197
|
+
})
|
198
|
+
case response.code
|
199
|
+
when 204
|
200
|
+
response
|
201
|
+
when 400
|
202
|
+
raise OpenTokArchiveError, "The request was invalid."
|
203
|
+
when 403
|
204
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
205
|
+
when 404
|
206
|
+
raise OpenTokArchiveError, "No matching archive found with the specified ID: #{archive_id}"
|
207
|
+
when 405
|
208
|
+
raise OpenTokArchiveError, "The archive was started with streamMode set to 'auto', which does not support stream manipulation."
|
209
|
+
when 500
|
210
|
+
raise OpenTokError, "OpenTok server error."
|
211
|
+
else
|
212
|
+
raise OpenTokArchiveError, "The archive streams could not be updated."
|
213
|
+
end
|
214
|
+
rescue StandardError => e
|
215
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
216
|
+
end
|
217
|
+
|
191
218
|
def forceDisconnect(session_id, connection_id)
|
192
219
|
response = self.class.delete("/v2/project/#{@api_key}/session/#{session_id}/connection/#{connection_id}", {
|
193
220
|
:headers => generate_headers("Content-Type" => "application/json")
|
@@ -206,6 +233,45 @@ module OpenTok
|
|
206
233
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
207
234
|
end
|
208
235
|
|
236
|
+
def force_mute_stream(session_id, stream_id)
|
237
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/stream/#{stream_id}/mute", {
|
238
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
239
|
+
})
|
240
|
+
case response.code
|
241
|
+
when 200
|
242
|
+
response
|
243
|
+
when 400
|
244
|
+
raise ArgumentError, "Force mute failed. Stream ID #{stream_id} or Session ID #{session_id} is invalid"
|
245
|
+
when 403
|
246
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
247
|
+
when 404
|
248
|
+
raise OpenTokConnectionError, "Either Stream ID #{stream_id} or Session ID #{session_id} is invalid"
|
249
|
+
end
|
250
|
+
rescue StandardError => e
|
251
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
252
|
+
end
|
253
|
+
|
254
|
+
def force_mute_session(session_id, opts)
|
255
|
+
opts.extend(HashExtensions)
|
256
|
+
body = opts.camelize_keys!
|
257
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/mute", {
|
258
|
+
:body => body.to_json,
|
259
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
260
|
+
})
|
261
|
+
case response.code
|
262
|
+
when 200
|
263
|
+
response
|
264
|
+
when 400
|
265
|
+
raise ArgumentError, "Force mute failed. The request could not be processed due to a bad request"
|
266
|
+
when 403
|
267
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
268
|
+
when 404
|
269
|
+
raise OpenTokConnectionError, "Session ID #{session_id} is invalid"
|
270
|
+
end
|
271
|
+
rescue StandardError => e
|
272
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
273
|
+
end
|
274
|
+
|
209
275
|
def signal(session_id, connection_id, opts)
|
210
276
|
opts.extend(HashExtensions)
|
211
277
|
connectionPath = connection_id.to_s.empty? ? "" : "/connection/#{connection_id}"
|
@@ -257,6 +323,52 @@ module OpenTok
|
|
257
323
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
258
324
|
end
|
259
325
|
|
326
|
+
def play_dtmf_to_connection(session_id, connection_id, dtmf_digits)
|
327
|
+
body = { "digits" => dtmf_digits }
|
328
|
+
|
329
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/connection/#{connection_id}/play-dtmf", {
|
330
|
+
:body => body.to_json,
|
331
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
332
|
+
})
|
333
|
+
case response.code
|
334
|
+
when 200
|
335
|
+
response
|
336
|
+
when 400
|
337
|
+
raise ArgumentError, "One of the properties — dtmf_digits #{dtmf_digits} or session_id #{session_id} — is invalid."
|
338
|
+
when 403
|
339
|
+
raise OpenTokAuthenticationError, "Authentication failed. This can occur if you use an invalid OpenTok API key or an invalid JSON web token. API Key: #{@api_key}"
|
340
|
+
when 404
|
341
|
+
raise OpenTokError, "The specified session #{session_id} does not exist or the client specified by the #{connection_id} property is not connected to the session."
|
342
|
+
else
|
343
|
+
raise OpenTokError, "An error occurred when attempting to play DTMF digits to the session"
|
344
|
+
end
|
345
|
+
rescue StandardError => e
|
346
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
347
|
+
end
|
348
|
+
|
349
|
+
def play_dtmf_to_session(session_id, dtmf_digits)
|
350
|
+
body = { "digits" => dtmf_digits }
|
351
|
+
|
352
|
+
response = self.class.post("/v2/project/#{@api_key}/session/#{session_id}/play-dtmf", {
|
353
|
+
:body => body.to_json,
|
354
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
355
|
+
})
|
356
|
+
case response.code
|
357
|
+
when 200
|
358
|
+
response
|
359
|
+
when 400
|
360
|
+
raise ArgumentError, "One of the properties — dtmf_digits #{dtmf_digits} or session_id #{session_id} — is invalid."
|
361
|
+
when 403
|
362
|
+
raise OpenTokAuthenticationError, "Authentication failed. This can occur if you use an invalid OpenTok API key or an invalid JSON web token. API Key: #{@api_key}"
|
363
|
+
when 404
|
364
|
+
raise OpenTokError, "The specified session does not exist. Session ID: #{session_id}"
|
365
|
+
else
|
366
|
+
raise OpenTokError, "An error occurred when attempting to play DTMF digits to the session"
|
367
|
+
end
|
368
|
+
rescue StandardError => e
|
369
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
370
|
+
end
|
371
|
+
|
260
372
|
def info_stream(session_id, stream_id)
|
261
373
|
streamId = stream_id.to_s.empty? ? '' : "/#{stream_id}"
|
262
374
|
url = "/v2/project/#{@api_key}/session/#{session_id}/stream#{streamId}"
|
@@ -371,6 +483,28 @@ module OpenTok
|
|
371
483
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
372
484
|
end
|
373
485
|
|
486
|
+
def list_broadcasts(offset, count, session_id)
|
487
|
+
query = Hash.new
|
488
|
+
query[:offset] = offset unless offset.nil?
|
489
|
+
query[:count] = count unless count.nil?
|
490
|
+
query[:sessionId] = session_id unless session_id.nil?
|
491
|
+
response = self.class.get("/v2/project/#{@api_key}/broadcast", {
|
492
|
+
:query => query.empty? ? nil : query,
|
493
|
+
:headers => generate_headers,
|
494
|
+
})
|
495
|
+
case response.code
|
496
|
+
when 200
|
497
|
+
response
|
498
|
+
when 403
|
499
|
+
raise OpenTokAuthenticationError,
|
500
|
+
"Authentication failed while retrieving broadcasts. API Key: #{@api_key}"
|
501
|
+
else
|
502
|
+
raise OpenTokBroadcastError, "The broadcasts could not be retrieved."
|
503
|
+
end
|
504
|
+
rescue StandardError => e
|
505
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
506
|
+
end
|
507
|
+
|
374
508
|
def layout_broadcast(broadcast_id, opts)
|
375
509
|
opts.extend(HashExtensions)
|
376
510
|
response = self.class.put("/v2/project/#{@api_key}/broadcast/#{broadcast_id}/layout", {
|
@@ -393,5 +527,32 @@ module OpenTok
|
|
393
527
|
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
394
528
|
end
|
395
529
|
|
530
|
+
def select_streams_for_broadcast(broadcast_id, opts)
|
531
|
+
opts.extend(HashExtensions)
|
532
|
+
body = opts.camelize_keys!
|
533
|
+
response = self.class.patch("/v2/project/#{@api_key}/broadcast/#{broadcast_id}/streams", {
|
534
|
+
:body => body.to_json,
|
535
|
+
:headers => generate_headers("Content-Type" => "application/json")
|
536
|
+
})
|
537
|
+
case response.code
|
538
|
+
when 204
|
539
|
+
response
|
540
|
+
when 400
|
541
|
+
raise OpenTokBroadcastError, "The request was invalid."
|
542
|
+
when 403
|
543
|
+
raise OpenTokAuthenticationError, "Authentication failed. API Key: #{@api_key}"
|
544
|
+
when 404
|
545
|
+
raise OpenTokBroadcastError, "No matching broadcast found with the specified ID: #{broadcast_id}"
|
546
|
+
when 405
|
547
|
+
raise OpenTokBroadcastError, "The broadcast was started with streamMode set to 'auto', which does not support stream manipulation."
|
548
|
+
when 500
|
549
|
+
raise OpenTokError, "OpenTok server error."
|
550
|
+
else
|
551
|
+
raise OpenTokBroadcastError, "The broadcast streams could not be updated."
|
552
|
+
end
|
553
|
+
rescue StandardError => e
|
554
|
+
raise OpenTokError, "Failed to connect to OpenTok. Response code: #{e.message}"
|
555
|
+
end
|
556
|
+
|
396
557
|
end
|
397
558
|
end
|
data/lib/opentok/connections.rb
CHANGED
data/lib/opentok/constants.rb
CHANGED
data/lib/opentok/opentok.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require "resolv"
|
2
|
+
require "set"
|
3
|
+
|
1
4
|
require "opentok/constants"
|
2
5
|
require "opentok/session"
|
3
6
|
require "opentok/client"
|
@@ -9,9 +12,6 @@ require "opentok/streams"
|
|
9
12
|
require "opentok/signals"
|
10
13
|
require "opentok/broadcasts"
|
11
14
|
|
12
|
-
require "resolv"
|
13
|
-
require "set"
|
14
|
-
|
15
15
|
module OpenTok
|
16
16
|
# Contains methods for creating OpenTok sessions and generating tokens. It also includes
|
17
17
|
# methods for returning object that let you work with archives, work with live streaming
|
@@ -42,9 +42,9 @@ module OpenTok
|
|
42
42
|
# streams, and signal. (This is the default value if you do not specify a role.)
|
43
43
|
#
|
44
44
|
# * <code>:moderator</code> -- In addition to the privileges granted to a
|
45
|
-
# publisher,
|
46
|
-
#
|
47
|
-
#
|
45
|
+
# publisher, a moderator can perform moderation functions, such as forcing clients
|
46
|
+
# to disconnect, to stop publishing streams, or to mute audio in published streams. See the
|
47
|
+
# {https://tokbox.com/developer/guides/moderation/ Moderation developer guide}.
|
48
48
|
# @option options [integer] :expire_time The expiration time, in seconds since the UNIX epoch.
|
49
49
|
# Pass in 0 to use the default expiration time of 24 hours after the token creation time.
|
50
50
|
# The maximum expiration time is 30 days after the creation time.
|
@@ -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
|
81
|
-
# @option opts [Symbol] :ua_addendum Do not set this parameter. It is for internal use by
|
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
|
38
|
+
# @option opts [true, false] :secure Whether the media must be transmitted
|
37
39
|
# encrypted (true) or not (false, the default).
|
40
|
+
# @option opts [true, false] :video Whether the SIP call will include
|
41
|
+
# video (true) or not (false, the default). With video included, the SIP
|
42
|
+
# client's video is included in the OpenTok stream that is sent to the
|
43
|
+
# OpenTok session. The SIP client will receive a single composed video of
|
44
|
+
# the published streams in the OpenTok session.
|
45
|
+
# @option opts [true, false] :observe_force_mute Whether the SIP end point
|
46
|
+
# observes {https://tokbox.com/developer/guides/moderation/#force_mute force mute moderation}
|
47
|
+
# (true) or not (false, the default).
|
38
48
|
def dial(session_id, token, sip_uri, opts)
|
39
49
|
response = @client.dial(session_id, token, sip_uri, opts)
|
40
50
|
end
|
41
51
|
|
52
|
+
# Sends DTMF digits to a specific client connected to an OpnTok session.
|
53
|
+
#
|
54
|
+
# @param [String] session_id The session ID.
|
55
|
+
# @param [String] connection_id The connection ID of the specific connection that
|
56
|
+
# the DTMF signal is being sent to.
|
57
|
+
# @param [String] dtmf_digits The DTMF digits to send. This can include 0-9, "*", "#", and "p".
|
58
|
+
# A p indicates a pause of 500ms (if you need to add a delay in sending the digits).
|
59
|
+
def play_dtmf_to_connection(session_id, connection_id, dtmf_digits)
|
60
|
+
raise ArgumentError, "invalid DTMF digits" unless dtmf_digits_valid?(dtmf_digits)
|
61
|
+
response = @client.play_dtmf_to_connection(session_id, connection_id, dtmf_digits)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sends DTMF digits to all clients connected to an OpnTok session.
|
65
|
+
#
|
66
|
+
# @param [String] session_id The session ID.
|
67
|
+
# @param [String] dtmf_digits The DTMF digits to send. This can include 0-9, "*", "#", and "p".
|
68
|
+
# A p indicates a pause of 500ms (if you need to add a delay in sending the digits).
|
69
|
+
def play_dtmf_to_session(session_id, dtmf_digits)
|
70
|
+
raise ArgumentError, "invalid DTMF digits" unless dtmf_digits_valid?(dtmf_digits)
|
71
|
+
response = @client.play_dtmf_to_session(session_id, dtmf_digits)
|
72
|
+
end
|
73
|
+
|
42
74
|
def initialize(client)
|
43
75
|
@client = client
|
44
76
|
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def dtmf_digits_valid?(dtmf_digits)
|
81
|
+
dtmf_digits.match?(/^[0-9*#p]+$/)
|
82
|
+
end
|
45
83
|
end
|
46
84
|
end
|
data/lib/opentok/streams.rb
CHANGED
@@ -75,5 +75,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
|
data/lib/opentok/version.rb
CHANGED
data/opentok.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
|
19
19
|
bundler_version = RUBY_VERSION < '2.1' ? '~> 1.5' : '>= 1.5'
|
20
20
|
spec.add_development_dependency "bundler", bundler_version
|
21
|
-
spec.add_development_dependency "rake", "~> 12.
|
21
|
+
spec.add_development_dependency "rake", "~> 12.3.3"
|
22
22
|
spec.add_development_dependency "rspec", "~> 3.9.0"
|
23
23
|
spec.add_development_dependency "webmock", ">= 2.3.2"
|
24
24
|
spec.add_development_dependency "vcr", ">= 2.8.0"
|