bigbluebutton-Github/record-and-playback/core/scripts/rap-worker.rb
Calvin Walton efb44c9dd9 Rewrite rap-worker script in an optimized fashion
It now does much less directory reading, and performance should
scale far better with large numbers of recordings.

Semantics should be mostly unchanged, but there's greater use
of '.fail' files to mark errors, and '.done' files are now removed
after all of the following processing steps complete.

The rap worker no longer relies on processing scripts leaving
behind empty directories; those are now removed where appropriate.
2014-07-21 14:58:46 -04:00

223 lines
7.6 KiB
Ruby
Executable File

#!/usr/bin/ruby
# encoding: UTF-8
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# 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
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
require '../lib/recordandplayback'
require 'rubygems'
require 'yaml'
require 'fileutils'
def archive_recorded_meeting(recording_dir)
recorded_done_files = Dir.glob("#{recording_dir}/status/recorded/*.done")
recorded_done_files.each do |recorded_done|
match = /([^\/]*).done$/.match(recorded_done)
meeting_id = match[1]
archived_done = "#{recording_dir}/status/archived/#{meeting_id}.done"
next if File.exists?(archived_done)
archived_fail = "#{recording_dir}/status/archived/#{meeting_id}.fail"
next if File.exists?(archived_fail)
ret = BigBlueButton.exec_ret("ruby", "archive/archive.rb", "-m", meeting_id)
if ret == 0 && File.exists?(archived_done)
BigBlueButton.logger.info("Successfully archived #{meeting_id}")
FileUtils.rm(recorded_done)
else
BigBlueButton.logger.error("Failed to archive #{meeting_id}")
FileUtils.touch(archived_fail)
end
end
end
def sanity_archived_meeting(recording_dir)
archived_done_files = Dir.glob("#{recording_dir}/status/archived/*.done")
archived_done_files.each do |archived_done|
match = /([^\/]*).done$/.match(archived_done)
meeting_id = match[1]
sanity_done = "#{recording_dir}/status/sanity/#{meeting_id}.done"
next if File.exists?(sanity_done)
sanity_fail = "#{recording_dir}/status/sanity/#{meeting_id}.fail"
next if File.exists?(sanity_fail)
ret = BigBlueButton.exec_ret("ruby", "sanity/sanity.rb", "-m", meeting_id)
if ret == 0 && File.exists?(sanity_done)
BigBlueButton.logger.info("Successfully sanity checked #{meeting_id}")
post_archive(meeting_id)
FileUtils.rm(archived_done)
else
BigBlueButton.logger.error("Sanity check failed on #{meeting_id}")
FileUtils.touch(sanity_fail)
end
end
end
def process_archived_meeting(recording_dir)
sanity_done_files = Dir.glob("#{recording_dir}/status/sanity/*.done")
sanity_done_files.each do |sanity_done|
match = /([^\/]*).done$/.match(sanity_done)
meeting_id = match[1]
process_succeeded = true
# Iterate over the list of recording processing scripts to find available types
# For now, we look for the ".rb" extension - TODO other scripting languages?
Dir.glob("process/*.rb").sort.each do |process_script|
match2 = /([^\/]*).rb$/.match(process_script)
process_type = match2[1]
processed_done = "#{recording_dir}/status/processed/#{meeting_id}-#{process_type}.done"
next if File.exists?(processed_done)
processed_fail = "#{recording_dir}/status/processed/#{meeting_id}-#{process_type}.fail"
if File.exists?(processed_fail)
process_succeeded = false
next
end
# If the process directory doesn't exist, the script does nothing
FileUtils.rm_rf("#{recording_dir}/process/#{process_type}/#{meeting_id}")
process_start = Time.now
ret = BigBlueButton.exec_ret("ruby", process_script, "-m", meeting_id)
process_stop = Time.now
process_time = ((process_stop - process_start) * 1000).to_i
IO.write("#{recording_dir}/process/#{process_type}/#{meeting_id}/processing_time", process_time)
if ret == 0 and File.exists?(processed_done)
BigBlueButton.logger.info("Process format #{process_type} succeeded for #{meeting_id}")
BigBlueButton.logger.info("Process took #{process_time}ms")
else
BigBlueButton.logger.info("Process format #{process_type} failed for #{meeting_id}")
BigBlueButton.logger.info("Process took #{process_time}ms")
FileUtils.touch(processed_fail)
process_succeeded = false
end
end
if process_succeeded
post_process(meeting_id)
FileUtils.rm(sanity_done)
end
end
end
def publish_processed_meeting(recording_dir)
processed_done_files = Dir.glob("#{recording_dir}/status/processed/*.done")
processed_done_files.each do |processed_done|
match = /([^\/]*)-([^\/-]*).done$/.match(processed_done)
meeting_id = match[1]
process_type = match[2]
publish_succeeded = true
Dir.glob("publish/*.rb").sort.each do |publish_script|
match2 = /([^\/]*).rb$/.match(publish_script)
publish_type = match2[1]
published_done = "#{recording_dir}/status/published/#{meeting_id}-#{publish_type}.done"
next if File.exists?(published_done)
published_fail = "#{recording_dir}/status/published/#{meeting_id}-#{publish_type}.fail"
if File.exists?(published_fail)
publish_succeeded = false
next
end
# If the publish directory doesn't exist, the script does nothing
FileUtils.rm_rf("#{recording_dir}/publish/#{publish_type}/#{meeting_id}")
# For legacy reasons, the meeting ID passed to the publish script contains
# the playback format name.
ret = BigBlueButton.exec_ret("ruby", publish_script, "-m", "#{meeting_id}-#{publish_type}")
if ret == 0 and File.exists?(published_done)
BigBlueButton.logger.info("Publish format #{publish_type} succeeded for #{meeting_id}")
else
BigBlueButton.logger.info("Publish format #{publish_type} failed for #{meeting_id}")
FileUtils.touch(published_fail)
publish_succeeded = false
end
end
if publish_succeeded
post_publish(meeting_id)
processed_done_files.each do |processed_done|
FileUtils.rm(processed_done)
end
# Also clean up the publish and process work files
Dir.glob("process/*.rb").sort.each do |process_script|
match2 = /([^\/]*).rb$/.match(process_script)
process_type = match2[1]
FileUtils.rm_rf("#{recording_dir}/process/#{process_type}/#{meeting_id}")
end
Dir.glob("publish/*.rb").sort.each do |publish_script|
match2 = /([^\/]*).rb$/.match(publish_script)
publish_type = match2[1]
FileUtils.rm_rf("#{recording_dir}/publish/#{publish_type}/#{meeting_id}")
end
end
end
end
def post_archive(meeting_id)
BigBlueButton.exec_ret("ruby", "post_scripts/post_archive.rb", "-m", meeting_id)
end
def post_process(meeting_id)
BigBlueButton.exec_ret("ruby", "post_scripts/post_process.rb", "-m", meeting_id)
end
def post_publish(meeting_id)
BigBlueButton.exec_ret("ruby", "post_scripts/post_publish.rb", "-m", meeting_id)
end
begin
props = YAML::load(File.open('bigbluebutton.yml'))
log_dir = props['log_dir']
recording_dir = props['recording_dir']
logger = Logger.new("#{log_dir}/bbb-rap-worker.log",'daily' )
logger.level = Logger::DEBUG
BigBlueButton.logger = logger
BigBlueButton.logger.debug("Running rap-worker...")
archive_recorded_meeting(recording_dir)
sanity_archived_meeting(recording_dir)
process_archived_meeting(recording_dir)
publish_processed_meeting(recording_dir)
rescue Exception => e
BigBlueButton.logger.error(e.message)
e.backtrace.each do |traceline|
BigBlueButton.logger.error(traceline)
end
end