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.