Hope you are doing well.
I am facing an issue with post publish recording ready callback script.
I will share some context to be all on same page.
I am using bbb 2.5, after enabling the recording feature. I noticed bbb 2.5 all files are stored as raw. After some research, I found this project bbb-recording-exporter
I used it and it works fine, after that I thought it would be better to upload the recording file to S3 so I updated this file:
/usr/local/bigbluebutton/core/scripts/post_publish/post_publish_recording_ready_callback.rb
to upload the file after being published.
#!/usr/bin/ruby
# encoding: UTF-8
#
#
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3.0 of the License, or (at your option)
# any later version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
#
require "optimist"
require 'net/http'
require "jwt"
require "java_properties"
require File.expand_path('../../../lib/recordandplayback', __FILE__)
require "date"
require "aws-sdk-s3"
require "securerandom"
require "streamio-ffmpeg"
logger = Logger.new("/var/log/bigbluebutton/post_publish.log", 'weekly' )
logger.level = Logger::INFO
BigBlueButton.logger = logger
opts = Optimist::options do
opt :meeting_id, "Meeting id to archive", :type => String
opt :format, "Playback format name", :type => String
end
meeting_id = opts[:meeting_id]
bbb_web_properties = "/etc/bigbluebutton/bbb-web.properties"
events_xml = "/var/bigbluebutton/recording/raw/#{meeting_id}/events.xml"
def get_metadata(key, meeting_metadata)
meeting_metadata.key?(key) ? meeting_metadata[key].value : nil
end
def get_callback_url(events_xml)
meeting_metadata = BigBlueButton::Events.get_meeting_metadata(events_xml)
meta_bbb_rec_ready_url = "bbb-recording-ready-url"
callback_url = get_metadata(meta_bbb_rec_ready_url, meeting_metadata)
# For compatibility with some 3rd party implementations, look up for
# bn-recording-ready-url or canvas-recording-ready, when bbb-recording-ready
# is not included.
meta_bn_rec_ready_url = "bn-recording-ready-url"
meta_canvas_rec_ready_url = "canvas-recording-ready-url"
callback_url ||= get_metadata(meta_bn_rec_ready_url, meeting_metadata)
callback_url ||= get_metadata(meta_canvas_rec_ready_url, meeting_metadata)
callback_url
end
def upload_recording_to_object_storage(metadata)
BigBlueButton.logger.info("Uploading recording to object storage") props = JavaProperties::Properties.new(bbb_web_properties)
endpoint = props[:s3Endpoint]
access_key_id = props[:s3AccessKey]
secret_access_key = props[:s3SecretKey]
bucket_name = props[:s3BucketName]
object_path = props[:s3ObjectPath]
object_name = props[:s3ObjectName]
s3_client = Aws::S3::Client.new(
endpoint: endpoint,
access_key_id: access_key_id,
secret_access_key: secret_access_key,
region: 'us-east-1',
force_path_style: true)
uuid = SecureRandom.uuid
bucket_name = bucket_name
object_path = object_path + '/' + metadata[:recordId] + '/' + object_name
object_key = uuid + '.mp4'
metadata = {
'meetingId' => metadata[:meetingId],
'recordId' => metadata[:recordId],
'title' => metadata[:title],
'duration' => metadata[:duration],
'owner' => metadata[:owner],
'uuid' => uuid,
}
s3_client.put_object(bucket: bucket_name, key: object_key, body: File.open(object_path), metadata: metadata)
BigBlueButton.logger.info("File '#{object_key}' uploaded successfully to bucket '#{bucket_name}'.") rescue Aws::S3::Errors::ServiceError => e
BigBlueButton.logger.info("Failed to upload recording: #{e}") end
#
# Main code
#
BigBlueButton.logger.info("Recording Ready Notify for [#{meeting_id}] starts") begin
callback_url = get_callback_url(events_xml)
unless callback_url.nil?
BigBlueButton.logger.info("Making callback for recording ready notification") meeting_metadata = BigBlueButton::Events.get_meeting_metadata(events_xml)
BigBlueButton.logger.info("metadate #{meeting_metadata}") props = JavaProperties::Properties.new(bbb_web_properties)
secret = props[:securitySalt]
external_meeting_id = BigBlueButton::Events.get_external_meeting_id(events_xml)
BigBlueButton.logger.info("external_meeting_id: #{external_meeting_id}") meeting_title = get_metadata('title',meeting_metadata)
meeting_owner = get_metadata('owner',meeting_metadata)
recording_path = object_path + '/' + meeting_id + '/' + object_name
recording = FFMPEG::Movie.new(recording_path)
meeting_duration = recording.duration
recording_date = Date.today.to_s
bbb_url= get_metadata('bbburl',meeting_metadata)
recording_url = "#{bbb_url}/presentation/#{meeting_id}/meeting.mp4"
payload = { meetingId: external_meeting_id, recordId: meeting_id, title: meeting_title, url: recording_url, date: recording_date, duration: meeting_duration, owner: meeting_owner }
upload_recording_to_object_storage(payload)
payload_encoded = JWT.encode(payload, secret)
uri = URI.parse(callback_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == 'https')
BigBlueButton.logger.info("Sending request to #{uri.scheme}://#{uri.host}#{uri.request_uri}") request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data(payload)
response = http.request(request)
code = response.code.to_i
if code == 410
BigBlueButton.logger.info("Notified for deleted meeting: #{meeting_id}") # TODO: should we automatically delete the recording here?
elsif code == 404
BigBlueButton.logger.info("404 error when notifying for recording: #{meeting_id}, ignoring") elsif code < 200 || code >= 300
BigBlueButton.logger.info("Callback HTTP request failed: #{response.code} #{response.message} (code #{code})") else
BigBlueButton.logger.info("Recording notifier successful: #{meeting_id} (code #{code})") end
end
rescue => e
end
BigBlueButton.logger.info("Recording Ready notify ends") exit 0
I tested the script to check if the package is installed, as a standalone, it works fine.