bigbluebutton_rails 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/CHANGELOG.md +34 -0
- data/Dockerfile +23 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +14 -5
- data/README.md +42 -59
- data/Rakefile +6 -3
- data/app/controllers/bigbluebutton/api/rooms_controller.rb +117 -0
- data/app/controllers/bigbluebutton/playback_types_controller.rb +0 -3
- data/app/controllers/bigbluebutton/recordings_controller.rb +37 -23
- data/app/controllers/bigbluebutton/rooms_controller.rb +6 -31
- data/app/controllers/bigbluebutton/servers_controller.rb +0 -14
- data/app/helpers/bigbluebutton_rails_helper.rb +4 -0
- data/app/models/bigbluebutton_attendee.rb +21 -0
- data/app/models/bigbluebutton_meeting.rb +5 -0
- data/app/models/bigbluebutton_playback_format.rb +3 -2
- data/app/models/bigbluebutton_recording.rb +164 -68
- data/app/models/bigbluebutton_room.rb +148 -59
- data/app/models/bigbluebutton_server.rb +1 -1
- data/app/views/bigbluebutton/api/error.rabl +10 -0
- data/app/views/bigbluebutton/api/rooms/index.rabl +45 -0
- data/app/views/bigbluebutton/api/rooms/join.rabl +6 -0
- data/app/views/bigbluebutton/api/rooms/running.rabl +10 -0
- data/app/views/bigbluebutton/recordings/play.html.erb +1 -0
- data/app/views/bigbluebutton/servers/_activity_list.html.erb +5 -5
- data/app/workers/{bigbluebutton_finish_meetings.rb → bigbluebutton_finish_meetings_worker.rb} +2 -2
- data/app/workers/{bigbluebutton_meeting_updater.rb → bigbluebutton_meeting_updater_worker.rb} +3 -3
- data/app/workers/bigbluebutton_recordings_for_room_worker.rb +27 -0
- data/app/workers/{bigbluebutton_update_recordings.rb → bigbluebutton_update_recordings_worker.rb} +2 -2
- data/app/workers/{bigbluebutton_update_server_configs.rb → bigbluebutton_update_server_configs_worker.rb} +3 -3
- data/bigbluebutton_rails.gemspec +1 -0
- data/config/locales/en.yml +21 -0
- data/config/locales/pt-br.yml +1 -0
- data/config/resque/resque.rake +7 -1
- data/config/resque/workers_schedule.yml +3 -3
- data/docker-compose.yml +68 -0
- data/dumb-init_1.2.0 +0 -0
- data/lib/bigbluebutton_rails.rb +1 -2
- data/lib/bigbluebutton_rails/api_controller_methods.rb +138 -0
- data/lib/bigbluebutton_rails/configuration.rb +33 -4
- data/lib/bigbluebutton_rails/controller_methods.rb +0 -31
- data/lib/bigbluebutton_rails/exceptions.rb +17 -0
- data/lib/bigbluebutton_rails/rails/routes.rb +14 -0
- data/lib/bigbluebutton_rails/version.rb +1 -1
- data/lib/generators/bigbluebutton_rails/templates/migration.rb +17 -3
- data/lib/generators/bigbluebutton_rails/templates/migration_2_2_0.rb +1 -1
- data/lib/generators/bigbluebutton_rails/templates/migration_2_2_0_b.rb +106 -0
- data/lib/generators/bigbluebutton_rails/templates/migration_2_3_0.rb +22 -0
- data/lib/tasks/bigbluebutton_rails/meetings.rake +1 -1
- data/spec/controllers/bigbluebutton/api/rooms_controller_spec.rb +498 -0
- data/spec/controllers/bigbluebutton/recordings_controller_spec.rb +84 -0
- data/spec/controllers/bigbluebutton/rooms_controller_exception_handling_spec.rb +2 -2
- data/spec/controllers/bigbluebutton/rooms_controller_spec.rb +24 -22
- data/spec/controllers/bigbluebutton/servers_controller_spec.rb +9 -0
- data/spec/factories/bigbluebutton_attendee.rb +7 -0
- data/spec/factories/bigbluebutton_meeting.rb +0 -1
- data/spec/factories/bigbluebutton_playback_type.rb +1 -0
- data/spec/factories/bigbluebutton_recording.rb +2 -4
- data/spec/helpers/bigbluebutton_rails_helper_spec.rb +10 -2
- data/spec/lib/bigbluebutton_rails/background_tasks_spec.rb +1 -0
- data/spec/lib/tasks/meetings_rake_spec.rb +1 -1
- data/spec/models/bigbluebutton_attendee_spec.rb +44 -0
- data/spec/models/bigbluebutton_meeting_db_spec.rb +0 -1
- data/spec/models/bigbluebutton_meeting_spec.rb +1 -0
- data/spec/models/bigbluebutton_playback_format_spec.rb +2 -0
- data/spec/models/bigbluebutton_playback_type_db_spec.rb +1 -0
- data/spec/models/bigbluebutton_recording_db_spec.rb +2 -2
- data/spec/models/bigbluebutton_recording_spec.rb +404 -72
- data/spec/models/bigbluebutton_room_spec.rb +435 -148
- data/spec/rails_app/config/database.yml.example +10 -11
- data/spec/rails_app/config/initializers/resque.rb +33 -0
- data/spec/rails_app/config/routes.rb +2 -0
- data/spec/rails_app/lib/tasks/db/populate.rake +73 -55
- data/spec/support/mocked_server.rb +1 -1
- data/spec/workers/{bigbluebutton_finish_meetings_spec.rb → bigbluebutton_finish_meetings_worker_spec.rb} +3 -3
- data/spec/workers/{bigbluebutton_meeting_updater_spec.rb → bigbluebutton_meeting_updater_worker_spec.rb} +8 -7
- data/spec/workers/bigbluebutton_recordings_for_room_worker_spec.rb +49 -0
- data/spec/workers/{bigbluebutton_update_recordings_spec.rb → bigbluebutton_update_recordings_worker_spec.rb} +3 -3
- data/spec/workers/{bigbluebutton_update_server_configs_spec.rb → bigbluebutton_update_server_configs_worker_spec.rb} +4 -4
- metadata +42 -14
- data/lib/classes/bigbluebutton_attendee.rb +0 -21
- data/spec/classes/bigbluebutton_attendee_spec.rb +0 -76
- data/spec/controllers/bigbluebutton/recordings_controller_json_responses_spec.rb +0 -111
- data/spec/controllers/bigbluebutton/rooms_controller_json_responses_spec.rb +0 -203
- data/spec/controllers/bigbluebutton/servers_controller_json_responses_spec.rb +0 -162
@@ -2,7 +2,6 @@ class Bigbluebutton::PlaybackTypesController < ApplicationController
|
|
2
2
|
include BigbluebuttonRails::InternalControllerMethods
|
3
3
|
|
4
4
|
respond_to :html
|
5
|
-
respond_to :json
|
6
5
|
before_filter :find_playback_type, only: [:update]
|
7
6
|
|
8
7
|
def update
|
@@ -12,13 +11,11 @@ class Bigbluebutton::PlaybackTypesController < ApplicationController
|
|
12
11
|
message = t('bigbluebutton_rails.playback_types.notice.update.success')
|
13
12
|
redirect_to_using_params request.referer, :notice => message
|
14
13
|
}
|
15
|
-
format.json { render :json => true, :status => :ok }
|
16
14
|
else
|
17
15
|
format.html {
|
18
16
|
flash[:error] = @playback_type.errors.full_messages.join(", ")
|
19
17
|
redirect_to_using_params request.referer
|
20
18
|
}
|
21
|
-
format.json { render :json => @playback_type.errors.full_messages, :status => :unprocessable_entity }
|
22
19
|
end
|
23
20
|
end
|
24
21
|
end
|
@@ -2,11 +2,21 @@ class Bigbluebutton::RecordingsController < ApplicationController
|
|
2
2
|
include BigbluebuttonRails::InternalControllerMethods
|
3
3
|
|
4
4
|
respond_to :html
|
5
|
-
respond_to :json, :only => [:index, :show, :update, :destroy, :publish, :unpublish]
|
6
5
|
before_filter :find_recording, :except => [:index]
|
7
6
|
before_filter :check_for_server, :only => [:publish, :unpublish]
|
8
7
|
before_filter :find_playback, :only => [:play]
|
9
8
|
|
9
|
+
layout :determine_layout
|
10
|
+
|
11
|
+
def determine_layout
|
12
|
+
case params[:action].to_sym
|
13
|
+
when :play
|
14
|
+
false
|
15
|
+
else
|
16
|
+
'application'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
10
20
|
def index
|
11
21
|
@recordings ||= BigbluebuttonRecording.all
|
12
22
|
respond_with(@recordings)
|
@@ -27,10 +37,8 @@ class Bigbluebutton::RecordingsController < ApplicationController
|
|
27
37
|
message = t('bigbluebutton_rails.recordings.notice.update.success')
|
28
38
|
redirect_to_using_params @recording, :notice => message
|
29
39
|
}
|
30
|
-
format.json { render :json => true, :status => :ok }
|
31
40
|
else
|
32
41
|
format.html { redirect_to_params_or_render :edit }
|
33
|
-
format.json { render :json => @recording.errors.full_messages, :status => :unprocessable_entity }
|
34
42
|
end
|
35
43
|
end
|
36
44
|
end
|
@@ -60,26 +68,29 @@ class Bigbluebutton::RecordingsController < ApplicationController
|
|
60
68
|
redirect_to_using_params bigbluebutton_recordings_url, :notice => message
|
61
69
|
end
|
62
70
|
}
|
63
|
-
format.json {
|
64
|
-
if error
|
65
|
-
render :json => { :message => message }, :status => :error
|
66
|
-
else
|
67
|
-
render :json => { :message => message }
|
68
|
-
end
|
69
|
-
}
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
74
|
def play
|
74
|
-
|
75
|
-
|
76
|
-
if
|
77
|
-
|
75
|
+
if @recording.present?
|
76
|
+
if @playback
|
77
|
+
if BigbluebuttonRails.configuration.playback_url_authentication
|
78
|
+
uri = @recording.token_url(bigbluebutton_user, request.remote_ip, @playback)
|
79
|
+
@playback_url = uri
|
78
80
|
else
|
79
|
-
|
80
|
-
redirect_to_using_params bigbluebutton_recording_url(@recording)
|
81
|
+
@playback_url = @playback.url
|
81
82
|
end
|
82
|
-
|
83
|
+
if @playback.downloadable? || !BigbluebuttonRails.configuration.playback_iframe
|
84
|
+
redirect_to @playback_url
|
85
|
+
end
|
86
|
+
# else will render the default 'play' view
|
87
|
+
else
|
88
|
+
flash[:error] = t('bigbluebutton_rails.recordings.errors.play.no_format')
|
89
|
+
redirect_to_using_params bigbluebutton_recording_url(@recording)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
flash[:error] = t('bigbluebutton_rails.recordings.errors.destroyed')
|
93
|
+
redirect_to my_home_path
|
83
94
|
end
|
84
95
|
end
|
85
96
|
|
@@ -105,7 +116,6 @@ class Bigbluebutton::RecordingsController < ApplicationController
|
|
105
116
|
flash[:error] = message
|
106
117
|
redirect_to bigbluebutton_recording_path(@recording)
|
107
118
|
}
|
108
|
-
format.json { render :json => message, :status => :error }
|
109
119
|
end
|
110
120
|
false
|
111
121
|
else
|
@@ -126,7 +136,6 @@ class Bigbluebutton::RecordingsController < ApplicationController
|
|
126
136
|
format.html {
|
127
137
|
redirect_to_using_params bigbluebutton_recording_path(@recording), :notice => message
|
128
138
|
}
|
129
|
-
format.json { render :json => message }
|
130
139
|
end
|
131
140
|
rescue BigBlueButton::BigBlueButtonException => e
|
132
141
|
respond_with do |format|
|
@@ -134,7 +143,6 @@ class Bigbluebutton::RecordingsController < ApplicationController
|
|
134
143
|
flash[:error] = e.to_s[0..200]
|
135
144
|
redirect_to_using_params bigbluebutton_recording_path(@recording)
|
136
145
|
}
|
137
|
-
format.json { render :json => e.to_s[0..200], :status => :error }
|
138
146
|
end
|
139
147
|
end
|
140
148
|
end
|
@@ -154,11 +162,17 @@ class Bigbluebutton::RecordingsController < ApplicationController
|
|
154
162
|
protected
|
155
163
|
|
156
164
|
def find_playback
|
157
|
-
if
|
158
|
-
|
165
|
+
if @recording.present?
|
166
|
+
if params[:type]
|
167
|
+
@playback = @recording.playback_formats.where(:playback_type_id => BigbluebuttonPlaybackType.find_by_identifier(params[:type])).first
|
168
|
+
else
|
169
|
+
@playback = @recording.default_playback_format || @recording.playback_formats.first
|
170
|
+
end
|
159
171
|
else
|
160
|
-
|
172
|
+
flash[:error] = t('bigbluebutton_rails.recordings.errors.destroyed')
|
173
|
+
redirect_to my_home_path
|
161
174
|
end
|
175
|
+
|
162
176
|
end
|
163
177
|
|
164
178
|
end
|
@@ -14,8 +14,8 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
14
14
|
before_filter :join_check_can_create, :only => :join
|
15
15
|
before_filter :join_check_redirect_to_mobile, :only => :join
|
16
16
|
|
17
|
-
respond_to :html, :
|
18
|
-
respond_to :json, :
|
17
|
+
respond_to :html, except: :running
|
18
|
+
respond_to :json, only: :running
|
19
19
|
|
20
20
|
def index
|
21
21
|
@rooms ||= BigbluebuttonRoom.all
|
@@ -50,15 +50,11 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
50
50
|
format.html {
|
51
51
|
redirect_to_using_params bigbluebutton_room_path(@room), :notice => message
|
52
52
|
}
|
53
|
-
format.json {
|
54
|
-
render :json => { :message => message }, :status => :created
|
55
|
-
}
|
56
53
|
else
|
57
54
|
format.html {
|
58
55
|
message = t('bigbluebutton_rails.rooms.notice.create.failure')
|
59
56
|
redirect_to_params_or_render :new, :error => message
|
60
57
|
}
|
61
|
-
format.json { render :json => @room.errors.full_messages, :status => :unprocessable_entity }
|
62
58
|
end
|
63
59
|
end
|
64
60
|
end
|
@@ -70,13 +66,11 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
70
66
|
format.html {
|
71
67
|
redirect_to_using_params bigbluebutton_room_path(@room), :notice => message
|
72
68
|
}
|
73
|
-
format.json { render :json => { :message => message } }
|
74
69
|
else
|
75
70
|
format.html {
|
76
71
|
message = t('bigbluebutton_rails.rooms.notice.update.failure')
|
77
72
|
redirect_to_params_or_render :edit, :error => message
|
78
73
|
}
|
79
|
-
format.json { render :json => @room.errors.full_messages, :status => :unprocessable_entity }
|
80
74
|
end
|
81
75
|
end
|
82
76
|
end
|
@@ -100,13 +94,6 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
100
94
|
flash[:error] = message if error
|
101
95
|
redirect_to_using_params bigbluebutton_rooms_url
|
102
96
|
}
|
103
|
-
format.json {
|
104
|
-
if error
|
105
|
-
render :json => { :message => message }, :status => :error
|
106
|
-
else
|
107
|
-
render :json => { :message => message }
|
108
|
-
end
|
109
|
-
}
|
110
97
|
end
|
111
98
|
end
|
112
99
|
|
@@ -163,14 +150,12 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
163
150
|
flash[:error] = message
|
164
151
|
redirect_to_using_params :back
|
165
152
|
}
|
166
|
-
format.json { render :json => message, :status => :error }
|
167
153
|
end
|
168
154
|
else
|
169
155
|
respond_with do |format|
|
170
156
|
format.html {
|
171
157
|
redirect_to_using_params bigbluebutton_room_path(@room), :notice => message
|
172
158
|
}
|
173
|
-
format.json { render :json => message }
|
174
159
|
end
|
175
160
|
end
|
176
161
|
|
@@ -224,7 +209,6 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
224
209
|
message = t('bigbluebutton_rails.rooms.notice.generate_dial_number.success')
|
225
210
|
respond_with do |format|
|
226
211
|
format.html { redirect_to_using_params :back, notice: message }
|
227
|
-
format.json { render json: true, status: :ok }
|
228
212
|
end
|
229
213
|
else
|
230
214
|
message = t('bigbluebutton_rails.rooms.errors.generate_dial_number.not_unique')
|
@@ -233,7 +217,6 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
233
217
|
flash[:error] = message
|
234
218
|
redirect_to_using_params :back
|
235
219
|
}
|
236
|
-
format.json { render json: { message: message }, status: :error }
|
237
220
|
end
|
238
221
|
end
|
239
222
|
end
|
@@ -241,7 +224,7 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
241
224
|
protected
|
242
225
|
|
243
226
|
def find_room
|
244
|
-
@room ||= BigbluebuttonRoom.
|
227
|
+
@room ||= BigbluebuttonRoom.find_by(param: params[:id])
|
245
228
|
end
|
246
229
|
|
247
230
|
def set_request_headers
|
@@ -336,8 +319,7 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
336
319
|
# first check if we have to create the room and if the user can do it
|
337
320
|
unless @room.fetch_is_running?
|
338
321
|
if bigbluebutton_can_create?(@room, role)
|
339
|
-
|
340
|
-
if @room.create_meeting(bigbluebutton_user, request, user_opts)
|
322
|
+
if @room.create_meeting(bigbluebutton_user, request)
|
341
323
|
logger.info "Meeting created: id: #{@room.meetingid}, name: #{@room.name}, created_by: #{username}, time: #{Time.now.iso8601}"
|
342
324
|
end
|
343
325
|
else
|
@@ -347,16 +329,9 @@ class Bigbluebutton::RoomsController < ApplicationController
|
|
347
329
|
end
|
348
330
|
end
|
349
331
|
|
350
|
-
# gets the token with the configurations for this user/room
|
351
|
-
token = @room.fetch_new_token
|
352
|
-
options = if token.nil? then {} else { :configToken => token } end
|
353
|
-
|
354
|
-
# set the create time and the user id, if they exist
|
355
|
-
options.merge!({ createTime: @room.create_time }) unless @room.create_time.blank?
|
356
|
-
options.merge!({ userID: id }) unless id.blank?
|
357
|
-
|
358
332
|
# room created and running, try to join it
|
359
|
-
url = @room.
|
333
|
+
url = @room.parameterized_join_url(username, role, id, {}, bigbluebutton_user)
|
334
|
+
|
360
335
|
unless url.nil?
|
361
336
|
|
362
337
|
# change the protocol to join with a mobile device
|
@@ -2,7 +2,6 @@ class Bigbluebutton::ServersController < ApplicationController
|
|
2
2
|
include BigbluebuttonRails::InternalControllerMethods
|
3
3
|
|
4
4
|
respond_to :html
|
5
|
-
respond_to :json, :only => [:index, :show, :new, :create, :update, :destroy, :activity, :rooms]
|
6
5
|
before_filter :find_server, :except => [:index, :new, :create]
|
7
6
|
|
8
7
|
def index
|
@@ -47,14 +46,8 @@ class Bigbluebutton::ServersController < ApplicationController
|
|
47
46
|
if error
|
48
47
|
flash[:error] = message
|
49
48
|
format.html { render :activity }
|
50
|
-
format.json {
|
51
|
-
array = @server.meetings
|
52
|
-
array.insert(0, { :message => message })
|
53
|
-
render :json => array, :status => :error
|
54
|
-
}
|
55
49
|
else
|
56
50
|
format.html
|
57
|
-
format.json
|
58
51
|
end
|
59
52
|
end
|
60
53
|
end
|
@@ -68,10 +61,8 @@ class Bigbluebutton::ServersController < ApplicationController
|
|
68
61
|
message = t('bigbluebutton_rails.servers.notice.create.success')
|
69
62
|
redirect_to_using_params @server, :notice => message
|
70
63
|
}
|
71
|
-
format.json { render :json => @server, :status => :created }
|
72
64
|
else
|
73
65
|
format.html { redirect_to_params_or_render :new }
|
74
|
-
format.json { render :json => @server.errors.full_messages, :status => :unprocessable_entity }
|
75
66
|
end
|
76
67
|
end
|
77
68
|
end
|
@@ -83,10 +74,8 @@ class Bigbluebutton::ServersController < ApplicationController
|
|
83
74
|
message = t('bigbluebutton_rails.servers.notice.update.success')
|
84
75
|
redirect_to_using_params @server, :notice => message
|
85
76
|
}
|
86
|
-
format.json { render :json => true, :status => :ok }
|
87
77
|
else
|
88
78
|
format.html { redirect_to_params_or_render :edit }
|
89
|
-
format.json { render :json => @server.errors.full_messages, :status => :unprocessable_entity }
|
90
79
|
end
|
91
80
|
end
|
92
81
|
end
|
@@ -97,7 +86,6 @@ class Bigbluebutton::ServersController < ApplicationController
|
|
97
86
|
|
98
87
|
respond_with do |format|
|
99
88
|
format.html { redirect_to_using_params bigbluebutton_servers_url }
|
100
|
-
format.json { render :json => true, :status => :ok }
|
101
89
|
end
|
102
90
|
end
|
103
91
|
|
@@ -174,7 +162,6 @@ class Bigbluebutton::ServersController < ApplicationController
|
|
174
162
|
end
|
175
163
|
redirect_to_using_params recordings_bigbluebutton_server_path(@server), :notice => message
|
176
164
|
}
|
177
|
-
format.json { render :json => message }
|
178
165
|
end
|
179
166
|
rescue BigBlueButton::BigBlueButtonException => e
|
180
167
|
respond_with do |format|
|
@@ -182,7 +169,6 @@ class Bigbluebutton::ServersController < ApplicationController
|
|
182
169
|
flash[:error] = e.to_s[0..200]
|
183
170
|
redirect_to_using_params recordings_bigbluebutton_server_path(@server)
|
184
171
|
}
|
185
|
-
format.json { render :json => e.to_s, :status => :error }
|
186
172
|
end
|
187
173
|
end
|
188
174
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class BigbluebuttonAttendee < ActiveRecord::Base
|
2
|
+
include ActiveModel::ForbiddenAttributesProtection
|
3
|
+
|
4
|
+
belongs_to :meeting, :class_name => 'BigbluebuttonMeeting',
|
5
|
+
:foreign_key => :bigbluebutton_meeting_id
|
6
|
+
|
7
|
+
validates :bigbluebutton_meeting_id, :presence => true
|
8
|
+
|
9
|
+
# TODO: no role on getStats yet, but it exists on getMeetings
|
10
|
+
attr_accessor :role
|
11
|
+
|
12
|
+
def duration
|
13
|
+
self.left_time - self.join_time
|
14
|
+
end
|
15
|
+
|
16
|
+
def from_hash(hash)
|
17
|
+
self.user_id = hash[:userID].to_s
|
18
|
+
self.user_name = hash[:fullName].to_s
|
19
|
+
self.role = hash[:role].to_s.downcase == "moderator" ? :moderator : :attendee
|
20
|
+
end
|
21
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
class BigbluebuttonMeeting < ActiveRecord::Base
|
2
3
|
include ActiveModel::ForbiddenAttributesProtection
|
3
4
|
|
@@ -8,6 +9,10 @@ class BigbluebuttonMeeting < ActiveRecord::Base
|
|
8
9
|
:foreign_key => 'meeting_id',
|
9
10
|
:dependent => :nullify
|
10
11
|
|
12
|
+
has_many :attendees,
|
13
|
+
:class_name => 'BigbluebuttonAttendee',
|
14
|
+
:dependent => :destroy
|
15
|
+
|
11
16
|
validates :room, :presence => true
|
12
17
|
|
13
18
|
validates :meetingid, :presence => true, :length => { :minimum => 1, :maximum => 100 }
|
@@ -4,8 +4,9 @@ class BigbluebuttonPlaybackFormat < ActiveRecord::Base
|
|
4
4
|
belongs_to :recording, :class_name => 'BigbluebuttonRecording'
|
5
5
|
belongs_to :playback_type, :class_name => 'BigbluebuttonPlaybackType'
|
6
6
|
|
7
|
-
delegate :name, :identifier, :visible, :visible?, :default, :default?,
|
8
|
-
|
7
|
+
delegate :name, :identifier, :visible, :visible?, :default, :default?,
|
8
|
+
:description, :downloadable, :downloadable?,
|
9
|
+
to: :playback_type, allow_nil: true
|
9
10
|
alias_attribute :format_type, :identifier
|
10
11
|
|
11
12
|
validates :recording_id, :presence => true
|
@@ -1,6 +1,8 @@
|
|
1
1
|
class BigbluebuttonRecording < ActiveRecord::Base
|
2
2
|
include ActiveModel::ForbiddenAttributesProtection
|
3
3
|
|
4
|
+
# NOTE: when adding new attributes to recordings, add them to `recording_changed?`
|
5
|
+
|
4
6
|
belongs_to :server, class_name: 'BigbluebuttonServer'
|
5
7
|
belongs_to :room, class_name: 'BigbluebuttonRoom'
|
6
8
|
belongs_to :meeting, class_name: 'BigbluebuttonMeeting'
|
@@ -23,15 +25,46 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
23
25
|
|
24
26
|
scope :published, -> { where(:published => true) }
|
25
27
|
|
28
|
+
serialize :recording_users, Array
|
29
|
+
|
26
30
|
def to_param
|
27
31
|
self.recordid
|
28
32
|
end
|
29
33
|
|
34
|
+
def get_token(user, ip)
|
35
|
+
server = BigbluebuttonServer.default
|
36
|
+
user.present? ? authName = user.username : authName = "anonymous"
|
37
|
+
api_token = server.api.send_api_request(:getRecordingToken, { authUser: authName, authAddr: ip, meetingID: self.recordid })
|
38
|
+
str_token = api_token[:token]
|
39
|
+
str_token
|
40
|
+
end
|
41
|
+
|
42
|
+
# Passing it on the url
|
43
|
+
#
|
44
|
+
def token_url(user, ip, playback)
|
45
|
+
auth_token = get_token(user, ip)
|
46
|
+
if auth_token.present?
|
47
|
+
uri = playback.url
|
48
|
+
uri += URI.parse(uri).query.blank? ? "?" : "&"
|
49
|
+
uri += "token=#{auth_token}"
|
50
|
+
uri
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
30
54
|
def default_playback_format
|
31
55
|
playback_formats.joins(:playback_type)
|
32
56
|
.where("bigbluebutton_playback_types.default = ?", true).first
|
33
57
|
end
|
34
58
|
|
59
|
+
# Remove this recording from the server
|
60
|
+
def delete_from_server!
|
61
|
+
if self.server.present?
|
62
|
+
self.server.send_delete_recordings(self.recordid)
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
35
68
|
# Returns the overall (i.e. for all recordings) average length of recordings in seconds
|
36
69
|
# Uses the length of the default playback format
|
37
70
|
def self.overall_average_length
|
@@ -47,6 +80,56 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
47
80
|
avg.nil? ? 0 : avg
|
48
81
|
end
|
49
82
|
|
83
|
+
# Compares a recording from the db with data from a getRecordings call.
|
84
|
+
# If anything changed in the recording, returns true.
|
85
|
+
# We select only the attributes that are saved and turn it all into sorted arrays
|
86
|
+
# to compare. If new attributes are stored in recordings, they should be added here.
|
87
|
+
#
|
88
|
+
# This was created to speed up the full sync of recordings.
|
89
|
+
# In the worst case the comparison is wrong and we're updating them all (same as
|
90
|
+
# not using this method at all, which is ok).
|
91
|
+
def self.recording_changed?(recording, data)
|
92
|
+
begin
|
93
|
+
# the attributes that are considered in the comparison
|
94
|
+
keys = [:end_time, :meetingid, :metadata, :playback, :published, :recordid, :size, :start_time] # rawSize is not stored at the moment
|
95
|
+
keys_formats = [:length, :type, :url] # :size, :processingTime are not stored at the moment
|
96
|
+
|
97
|
+
# the data from getRecordings
|
98
|
+
data_clone = data.deep_dup
|
99
|
+
data_clone[:size] = data_clone[:size].to_s if data_clone.key?(:size)
|
100
|
+
data_clone[:metadata] = data_clone[:metadata].sort if data_clone.key?(:metadata)
|
101
|
+
if data_clone.key?(:playback) && data_clone[:playback].key?(:format)
|
102
|
+
data_clone[:playback][:format] = [data_clone[:playback][:format]] unless data_clone[:playback][:format].is_a?(Array)
|
103
|
+
data_clone[:playback] = data_clone[:playback][:format].map{ |f|
|
104
|
+
f.slice(*keys_formats).sort
|
105
|
+
}.sort
|
106
|
+
else
|
107
|
+
data_clone[:playback] = []
|
108
|
+
end
|
109
|
+
data_clone[:end_time] = data_clone[:end_time].to_i if data_clone.key?(:end_time)
|
110
|
+
data_clone[:start_time] = data_clone[:start_time].to_i if data_clone.key?(:start_time)
|
111
|
+
data_clone = data_clone.slice(*keys)
|
112
|
+
data_sorted = data_clone.sort
|
113
|
+
|
114
|
+
# the data from the recording in the db
|
115
|
+
attrs = recording.attributes.symbolize_keys.slice(*keys)
|
116
|
+
attrs[:size] = attrs[:size].to_s if attrs.key?(:size)
|
117
|
+
attrs[:metadata] = recording.metadata.pluck(:name, :content).map{ |i| [i[0].to_sym, i[1]] }.sort
|
118
|
+
attrs[:playback] = recording.playback_formats.map{ |f|
|
119
|
+
r = f.attributes.symbolize_keys.slice(*keys_formats)
|
120
|
+
r[:type] = f.format_type
|
121
|
+
r.sort
|
122
|
+
}.sort
|
123
|
+
attrs = attrs.sort
|
124
|
+
|
125
|
+
# compare
|
126
|
+
data_sorted.to_s != attrs.to_s
|
127
|
+
rescue StandardError => e
|
128
|
+
logger.error "Error comparing recordings: #{e.inspect}"
|
129
|
+
true # always update recordings if on error
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
50
133
|
# Syncs the recordings in the db with the array of recordings in 'recordings',
|
51
134
|
# as received from BigBlueButtonApi#get_recordings.
|
52
135
|
# Will add new recordings that are not in the db yet and update the ones that
|
@@ -60,18 +143,24 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
60
143
|
recordings.each do |rec|
|
61
144
|
rec_obj = BigbluebuttonRecording.find_by_recordid(rec[:recordID])
|
62
145
|
rec_data = adapt_recording_hash(rec)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
146
|
+
changed = !rec_obj.present? ||
|
147
|
+
self.recording_changed?(rec_obj, rec_data)
|
148
|
+
|
149
|
+
if changed
|
150
|
+
BigbluebuttonRecording.transaction do
|
151
|
+
if rec_obj
|
152
|
+
logger.info "Sync recordings: updating recording #{rec_obj.inspect}"
|
153
|
+
logger.debug "Sync recordings: recording data #{rec_data.inspect}"
|
154
|
+
self.update_recording(server, rec_obj, rec_data)
|
155
|
+
else
|
156
|
+
logger.info "Sync recordings: creating recording"
|
157
|
+
logger.debug "Sync recordings: recording data #{rec_data.inspect}"
|
158
|
+
self.create_recording(server, rec_data)
|
159
|
+
end
|
72
160
|
end
|
73
161
|
end
|
74
162
|
end
|
163
|
+
cleanup_playback_types
|
75
164
|
|
76
165
|
# set as unavailable the recordings that are not in 'recordings', but
|
77
166
|
# only in a full synchronization process, which means that the recordings
|
@@ -107,6 +196,22 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
107
196
|
new_hash
|
108
197
|
end
|
109
198
|
|
199
|
+
def self.adapt_recording_users(original)
|
200
|
+
if original.present? && original.size > 0
|
201
|
+
users = original[:user]
|
202
|
+
users = [users] unless users.is_a?(Array)
|
203
|
+
users = users.map{ |u|
|
204
|
+
id = u[:externalUserID]
|
205
|
+
begin
|
206
|
+
id = Integer(id)
|
207
|
+
rescue
|
208
|
+
end
|
209
|
+
id
|
210
|
+
}
|
211
|
+
return users
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
110
215
|
# Updates the BigbluebuttonRecording 'recording' with the data in the hash 'data'.
|
111
216
|
# The format expected for 'data' follows the format returned by
|
112
217
|
# BigBlueButtonApi#get_recordings but with the keys already converted to our format.
|
@@ -115,6 +220,7 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
115
220
|
recording.room = BigbluebuttonRails.configuration.match_room_recording.call(data)
|
116
221
|
recording.attributes = data.slice(:meetingid, :name, :published, :start_time, :end_time, :size)
|
117
222
|
recording.available = true
|
223
|
+
recording.recording_users = adapt_recording_users(data[:recordingUsers])
|
118
224
|
recording.save!
|
119
225
|
|
120
226
|
sync_additional_data(recording, data)
|
@@ -129,8 +235,9 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
129
235
|
recording.available = true
|
130
236
|
recording.room = BigbluebuttonRails.configuration.match_room_recording.call(data)
|
131
237
|
recording.server = server
|
132
|
-
recording.description = I18n.t('bigbluebutton_rails.recordings.default.description', :time => recording.start_time.utc.to_formatted_s(:long))
|
238
|
+
recording.description = I18n.t('bigbluebutton_rails.recordings.default.description', :time => Time.at(recording.start_time).utc.to_formatted_s(:long))
|
133
239
|
recording.meeting = BigbluebuttonRecording.find_matching_meeting(recording)
|
240
|
+
recording.recording_users = adapt_recording_users(data[:recordingUsers])
|
134
241
|
recording.save!
|
135
242
|
|
136
243
|
sync_additional_data(recording, data)
|
@@ -154,73 +261,60 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
154
261
|
# keys are stored as strings in the db
|
155
262
|
received_metadata = metadata.clone.stringify_keys
|
156
263
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
meta_db.update_attributes({ :content => received_metadata[meta_db.name] })
|
163
|
-
received_metadata.delete(meta_db.name)
|
164
|
-
|
165
|
-
# the metadata is not in the received data, remove from the db
|
166
|
-
else
|
167
|
-
meta_db.destroy
|
168
|
-
end
|
169
|
-
end
|
264
|
+
# get all metadata for this recording
|
265
|
+
# note: it's a little slower than removing all metadata and adding again,
|
266
|
+
# but it's cleaner to just update it and the loss of performance is small
|
267
|
+
query = { owner_id: recording.id, owner_type: recording.class.to_s }
|
268
|
+
metas = BigbluebuttonMetadata.where(query).all
|
170
269
|
|
171
|
-
#
|
270
|
+
# batch insert all metadata
|
271
|
+
columns = [ :id, :name, :content, :owner_id, :owner_type ]
|
272
|
+
values = []
|
172
273
|
received_metadata.each do |name, content|
|
173
|
-
|
174
|
-
|
175
|
-
:content => content,
|
176
|
-
}
|
177
|
-
meta = BigbluebuttonMetadata.create(attrs)
|
178
|
-
meta.owner = recording
|
179
|
-
meta.save!
|
274
|
+
id = metas.select{ |m| m.name == name }.first.try(:id)
|
275
|
+
values << [ id, name, content, recording.id, recording.class.to_s ]
|
180
276
|
end
|
277
|
+
BigbluebuttonMetadata.import! columns, values, validate: true,
|
278
|
+
on_duplicate_key_update: [:name, :content]
|
279
|
+
|
280
|
+
# delete all that doesn't exist anymore
|
281
|
+
BigbluebuttonMetadata.where(query).where.not(name: received_metadata.keys).delete_all
|
181
282
|
end
|
182
283
|
|
183
284
|
# Syncs the playback formats objects of 'recording' with the data in 'formats'.
|
184
285
|
# The format expected for 'formats' follows the format returned by
|
185
286
|
# BigBlueButtonApi#get_recordings but with the keys already converted to our format.
|
186
287
|
def self.sync_playback_formats(recording, formats)
|
187
|
-
formats_copy = formats.clone
|
188
288
|
|
189
|
-
# make it an array if it's a hash with a single format
|
289
|
+
# clone and make it an array if it's a hash with a single format
|
290
|
+
formats_copy = formats.clone
|
190
291
|
formats_copy = [formats_copy] if formats_copy.is_a?(Hash)
|
191
292
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
# the format exists in the hash, update it in the db
|
198
|
-
if format
|
199
|
-
format_db.update_attributes({ url: format[:url], length: format[:length] })
|
200
|
-
formats_copy.delete(format)
|
201
|
-
|
202
|
-
# the format is not in the hash, remove from the db
|
203
|
-
else
|
204
|
-
format_db.destroy
|
205
|
-
end
|
206
|
-
end
|
293
|
+
# remove all formats for this recording
|
294
|
+
# note: easier than updating the formats because they don't have a clear key
|
295
|
+
# to match by
|
296
|
+
BigbluebuttonPlaybackFormat.where(recording_id: recording.id).delete_all
|
207
297
|
|
208
|
-
#
|
298
|
+
# batch insert all playback formats
|
299
|
+
columns = [ :recording_id, :url, :length , :playback_type_id ]
|
300
|
+
values = []
|
209
301
|
formats_copy.each do |format|
|
210
302
|
unless format[:type].blank?
|
211
|
-
playback_type = BigbluebuttonPlaybackType.
|
303
|
+
playback_type = BigbluebuttonPlaybackType.find_by(identifier: format[:type])
|
212
304
|
if playback_type.nil?
|
213
|
-
|
305
|
+
downloadable = BigbluebuttonRails.configuration.downloadable_playback_types.include?(format[:type])
|
306
|
+
attrs = {
|
307
|
+
identifier: format[:type],
|
308
|
+
visible: true,
|
309
|
+
downloadable: downloadable
|
310
|
+
}
|
214
311
|
playback_type = BigbluebuttonPlaybackType.create!(attrs)
|
215
312
|
end
|
216
313
|
|
217
|
-
|
218
|
-
length: format[:length].to_i, playback_type_id: playback_type.id }
|
219
|
-
BigbluebuttonPlaybackFormat.create!(attrs)
|
314
|
+
values << [ recording.id, format[:url], format[:length].to_i, playback_type.id ]
|
220
315
|
end
|
221
316
|
end
|
222
|
-
|
223
|
-
cleanup_playback_types
|
317
|
+
BigbluebuttonPlaybackFormat.import! columns, values, validate: true
|
224
318
|
end
|
225
319
|
|
226
320
|
# Remove the unused playback types from the list.
|
@@ -230,20 +324,22 @@ class BigbluebuttonRecording < ActiveRecord::Base
|
|
230
324
|
end
|
231
325
|
|
232
326
|
# Finds the BigbluebuttonMeeting that generated this recording. The meeting is searched using
|
233
|
-
# the
|
234
|
-
# the recording's ID.
|
327
|
+
# the meetingid associated with this recording and the create time of the meeting, taken from
|
328
|
+
# the recording's ID. There are some flexible clauses that try to match very close or truncated
|
329
|
+
# timestamps from recordings start times to meeting create times.
|
235
330
|
def self.find_matching_meeting(recording)
|
236
331
|
meeting = nil
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
332
|
+
unless recording.nil? #or recording.room.nil?
|
333
|
+
unless recording.start_time.nil?
|
334
|
+
start_time = recording.start_time
|
335
|
+
meeting = BigbluebuttonMeeting.where("meetingid = ? AND create_time = ?", recording.meetingid, start_time).last
|
336
|
+
if meeting.nil?
|
337
|
+
meeting = BigbluebuttonMeeting.where("meetingid = ? AND create_time DIV 1000 = ?", recording.meetingid, start_time).last
|
338
|
+
end
|
339
|
+
if meeting.nil?
|
340
|
+
div_start_time = (start_time/10)
|
341
|
+
meeting = BigbluebuttonMeeting.where("meetingid = ? AND create_time DIV 10 = ?", recording.meetingid, div_start_time).last
|
342
|
+
end
|
247
343
|
logger.info "Recording: meeting found for the recording #{recording.inspect}: #{meeting.inspect}"
|
248
344
|
end
|
249
345
|
end
|