Class: BigbluebuttonRecording

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
ActiveModel::ForbiddenAttributesProtection
Defined in:
app/models/bigbluebutton_recording.rb

Constant Summary collapse

STATES =
{
  processing: 'processing',
  processed: 'processed',
  published: 'published',
  unpublished: 'unpublished'
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.overall_average_lengthObject

Returns the overall (i.e. for all recordings) average length of recordings in seconds Uses the length of the default playback format



83
84
85
86
87
# File 'app/models/bigbluebutton_recording.rb', line 83

def self.overall_average_length
  avg = BigbluebuttonPlaybackFormat.joins(:playback_type)
        .where("bigbluebutton_playback_types.default = ?", true).average(:length)
  avg.nil? ? 0 : (avg.truncate(2) * 60)
end

.overall_average_sizeObject

Returns the overall (i.e. for all recordings) average size of recordings in bytes Uses the length of the default playback format



91
92
93
94
# File 'app/models/bigbluebutton_recording.rb', line 91

def self.overall_average_size
  avg = BigbluebuttonRecording.average(:size)
  avg.nil? ? 0 : avg
end

.recording_changed?(recording, data) ⇒ Boolean

Compares a recording from the db with data from a getRecordings call. If anything changed in the recording, returns true. We select only the attributes that are saved and turn it all into sorted arrays to compare. If new attributes are stored in recordings, they should be added here.

This was created to speed up the full sync of recordings. In the worst case the comparison is wrong and we’re updating them all (same as not using this method at all, which is ok).

Returns:

  • (Boolean)


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'app/models/bigbluebutton_recording.rb', line 104

def self.recording_changed?(recording, data)
  begin
    # the attributes that are considered in the comparison
    keys = [:end_time, :meetingid,  :metadata, :playback, :published, :recordid, :size, :start_time] # rawSize is not stored at the moment
    keys_formats = [:length, :type, :url] # :size, :processingTime are not stored at the moment

    # the data from getRecordings
    data_clone = data.deep_dup
    data_clone[:size] = data_clone[:size].to_s if data_clone.key?(:size)
    data_clone[:metadata] = data_clone[:metadata].sort if data_clone.key?(:metadata)
    if data_clone.key?(:playback) && data_clone[:playback].key?(:format)
      data_clone[:playback][:format] = [data_clone[:playback][:format]] unless data_clone[:playback][:format].is_a?(Array)
      data_clone[:playback] = data_clone[:playback][:format].map{ |f|
        f.slice(*keys_formats).sort
      }.sort
    else
      data_clone[:playback] = []
    end
    data_clone[:end_time] = data_clone[:end_time].to_i if data_clone.key?(:end_time)
    data_clone[:start_time] = data_clone[:start_time].to_i if data_clone.key?(:start_time)
    data_clone = data_clone.slice(*keys)
    data_sorted = data_clone.sort

    # the data from the recording in the db
    attrs = recording.attributes.symbolize_keys.slice(*keys)
    attrs[:size] = attrs[:size].to_s if attrs.key?(:size)
    attrs[:metadata] = recording..pluck(:name, :content).map{ |i| [i[0].to_sym, i[1]] }.sort
    attrs[:playback] = recording.playback_formats.map{ |f|
      r = f.attributes.symbolize_keys.slice(*keys_formats)
      r[:type] = f.format_type
      r.sort
    }.sort
    attrs = attrs.sort

    # compare
    data_sorted.to_s != attrs.to_s
  rescue StandardError => e
    logger.error "Error comparing recordings: #{e.inspect}"
    true # always update recordings if on error
  end
end

.sync(server, recordings, full_sync = false) ⇒ Object

Syncs the recordings in the db with the array of recordings in ‘recordings’, as received from BigBlueButtonApi#get_recordings. Will add new recordings that are not in the db yet and update the ones that already are (matching by ‘recordid’). Will NOT delete recordings from the db if they are not in the array but instead mark them as unavailable. ‘server’ is the BigbluebuttonServer object from which the recordings were fetched.

TODO: catch exceptions on creating/updating recordings



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'app/models/bigbluebutton_recording.rb', line 155

def self.sync(server, recordings, full_sync=false)
  recordings.each do |rec|
    rec_obj = BigbluebuttonRecording.find_by_recordid(rec[:recordID])
    rec_data = adapt_recording_hash(rec)
    changed = !rec_obj.present? ||
              self.recording_changed?(rec_obj, rec_data)

    if changed
      BigbluebuttonRecording.transaction do
        if rec_obj
          logger.info "Sync recordings: updating recording #{rec_obj.inspect}"
          logger.debug "Sync recordings: recording data #{rec_data.inspect}"
          self.update_recording(server, rec_obj, rec_data)
        else
          logger.info "Sync recordings: creating recording"
          logger.debug "Sync recordings: recording data #{rec_data.inspect}"
          self.create_recording(server, rec_data)
        end
      end
    end
  end
  cleanup_playback_types

  # set as unavailable the recordings that are not in 'recordings', but
  # only in a full synchronization process, which means that the recordings
  # in `recordings` are *all* available in `server`, not a subset.
  if full_sync
    recordIDs = recordings.map{ |rec| rec[:recordID] }
    if recordIDs.length <= 0 # empty response
      BigbluebuttonRecording.
        where(available: true, server: server).
        update_all(available: false)
    else
      BigbluebuttonRecording.
        where(available: true, server: server).
        where.not(recordid: recordIDs).
        update_all(available: false)
    end
  end
end

Instance Method Details

#default_playback_formatObject



67
68
69
70
# File 'app/models/bigbluebutton_recording.rb', line 67

def default_playback_format
  playback_formats.joins(:playback_type)
    .where("bigbluebutton_playback_types.default = ?", true).first
end

#delete_from_server!Object

Remove this recording from the server



73
74
75
76
77
78
79
# File 'app/models/bigbluebutton_recording.rb', line 73

def delete_from_server!
  if self.server.present?
    self.server.send_delete_recordings(self.recordid)
  else
    false
  end
end

#get_token(user, ip) ⇒ Object



47
48
49
50
51
52
53
# File 'app/models/bigbluebutton_recording.rb', line 47

def get_token(user, ip)
  server = BigbluebuttonServer.default
  user.present? ? authName = user.username : authName = "anonymous"
  api_token = server.api.send_api_request(:getRecordingToken, { authUser: authName, authAddr: ip, meetingID: self.recordid })
  str_token = api_token[:token]
  str_token
end

#is_published?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'app/models/bigbluebutton_recording.rb', line 43

def is_published?
  self.state.eql?(BigbluebuttonRecording::STATES[:published]) || self.state.eql?(BigbluebuttonRecording::STATES[:unpublished])
end

#to_paramObject



39
40
41
# File 'app/models/bigbluebutton_recording.rb', line 39

def to_param
  self.recordid
end

#token_url(user, ip, playback) ⇒ Object

Passing it on the url



57
58
59
60
61
62
63
64
65
# File 'app/models/bigbluebutton_recording.rb', line 57

def token_url(user, ip, playback)
  auth_token = get_token(user, ip)
  if auth_token.present?
    uri = playback.url
    uri += URI.parse(uri).query.blank? ? "?" : "&"
    uri += "token=#{auth_token}"
    uri
  end
end