2014-03-27 07:52:59 +08:00
|
|
|
#!/usr/bin/ruby
|
2012-05-04 02:56:01 +08:00
|
|
|
# encoding: UTF-8
|
|
|
|
|
2012-09-05 05:42:13 +08:00
|
|
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
|
|
|
#
|
|
|
|
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
2014-06-13 05:49:06 +08:00
|
|
|
# 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.
|
2012-09-05 05:42:13 +08:00
|
|
|
#
|
2014-06-13 05:49:06 +08:00
|
|
|
# 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.
|
2012-09-05 05:42:13 +08:00
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser General Public License along
|
|
|
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2014-07-28 23:02:38 +08:00
|
|
|
# Monit reduces the path, but we require tools that are often manually installed
|
|
|
|
# to /usr/local/bin. Add that to the path.
|
|
|
|
ENV['PATH'] += ':/usr/local/bin'
|
|
|
|
|
2011-05-14 05:45:59 +08:00
|
|
|
require '../lib/recordandplayback'
|
|
|
|
require 'rubygems'
|
|
|
|
require 'yaml'
|
|
|
|
require 'fileutils'
|
|
|
|
|
2014-11-12 21:13:57 +08:00
|
|
|
# Number of seconds to delay archiving (red5 race condition workaround)
|
|
|
|
ARCHIVE_DELAY_SECONDS = 120
|
2014-04-25 08:08:06 +08:00
|
|
|
|
2011-12-07 04:22:35 +08:00
|
|
|
def archive_recorded_meeting(recording_dir)
|
|
|
|
recorded_done_files = Dir.glob("#{recording_dir}/status/recorded/*.done")
|
2014-06-13 05:49:06 +08:00
|
|
|
|
2014-07-23 21:50:52 +08:00
|
|
|
FileUtils.mkdir_p("#{recording_dir}/status/archived")
|
2014-06-13 05:49:06 +08:00
|
|
|
recorded_done_files.each do |recorded_done|
|
|
|
|
match = /([^\/]*).done$/.match(recorded_done)
|
|
|
|
meeting_id = match[1]
|
2014-11-12 21:13:57 +08:00
|
|
|
|
|
|
|
if File.mtime(recorded_done) + ARCHIVE_DELAY_SECONDS > Time.now
|
|
|
|
BigBlueButton.logger.info("Temporarily skipping #{meeting_id} for Red5 race workaround")
|
|
|
|
next
|
|
|
|
end
|
2014-06-13 05:49:06 +08:00
|
|
|
|
|
|
|
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
|
2011-12-07 04:22:35 +08:00
|
|
|
end
|
2011-06-07 02:42:38 +08:00
|
|
|
end
|
|
|
|
|
2012-08-16 08:10:29 +08:00
|
|
|
def sanity_archived_meeting(recording_dir)
|
|
|
|
archived_done_files = Dir.glob("#{recording_dir}/status/archived/*.done")
|
|
|
|
|
2014-07-23 21:50:52 +08:00
|
|
|
FileUtils.mkdir_p("#{recording_dir}/status/sanity")
|
2014-06-13 05:49:06 +08:00
|
|
|
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
|
2012-08-16 08:10:29 +08:00
|
|
|
end
|
|
|
|
|
2014-04-25 08:08:06 +08:00
|
|
|
|
2011-06-07 02:42:38 +08:00
|
|
|
def process_archived_meeting(recording_dir)
|
2012-08-21 07:12:58 +08:00
|
|
|
sanity_done_files = Dir.glob("#{recording_dir}/status/sanity/*.done")
|
2014-06-13 05:49:06 +08:00
|
|
|
|
2014-07-23 21:50:52 +08:00
|
|
|
FileUtils.mkdir_p("#{recording_dir}/status/processed")
|
2014-06-13 05:49:06 +08:00
|
|
|
sanity_done_files.each do |sanity_done|
|
|
|
|
match = /([^\/]*).done$/.match(sanity_done)
|
2011-06-07 02:58:16 +08:00
|
|
|
meeting_id = match[1]
|
2014-04-25 08:08:06 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
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
|
2011-05-14 05:45:59 +08:00
|
|
|
end
|
|
|
|
|
2011-06-07 02:42:38 +08:00
|
|
|
def publish_processed_meeting(recording_dir)
|
2011-12-07 04:22:35 +08:00
|
|
|
processed_done_files = Dir.glob("#{recording_dir}/status/processed/*.done")
|
2014-06-13 05:49:06 +08:00
|
|
|
|
2014-07-23 21:50:52 +08:00
|
|
|
FileUtils.mkdir_p("#{recording_dir}/status/published")
|
2014-06-13 05:49:06 +08:00
|
|
|
processed_done_files.each do |processed_done|
|
|
|
|
match = /([^\/]*)-([^\/-]*).done$/.match(processed_done)
|
2011-06-07 02:58:16 +08:00
|
|
|
meeting_id = match[1]
|
2014-06-13 05:49:06 +08:00
|
|
|
process_type = match[2]
|
|
|
|
|
2014-10-08 23:08:22 +08:00
|
|
|
# Since we publish all formats for a recording at once, we want to skip duplicates
|
|
|
|
# if there were multiple publish format done files for the same recording
|
|
|
|
if !File.exists?(processed_done)
|
|
|
|
BigBlueButton.logger.info("Processed done file for #{meeting_id} #{process_type} gone, assuming already processed")
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
publish_succeeded = true
|
|
|
|
|
|
|
|
Dir.glob("publish/*.rb").sort.each do |publish_script|
|
|
|
|
match2 = /([^\/]*).rb$/.match(publish_script)
|
|
|
|
publish_type = match2[1]
|
2011-06-30 06:39:25 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
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
|
2011-06-07 02:58:16 +08:00
|
|
|
end
|
2014-06-13 05:49:06 +08:00
|
|
|
|
|
|
|
if publish_succeeded
|
|
|
|
post_publish(meeting_id)
|
2014-10-08 23:08:22 +08:00
|
|
|
# Clean up the process done files
|
2014-06-13 05:49:06 +08:00
|
|
|
# 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]
|
2014-10-08 23:08:22 +08:00
|
|
|
this_processed_done = "#{recording_dir}/status/processed/#{meeting_id}-#{process_type}.done"
|
|
|
|
FileUtils.rm(this_processed_done)
|
2014-06-13 05:49:06 +08:00
|
|
|
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
|
2011-06-07 02:42:38 +08:00
|
|
|
end
|
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
def post_archive(meeting_id)
|
2014-08-12 02:57:32 +08:00
|
|
|
Dir.glob("post_archive/*.rb").sort.each do |post_archive_script|
|
|
|
|
BigBlueButton.logger.info("Running post archive script #{post_archive_script}")
|
|
|
|
ret = BigBlueButton.exec_ret("ruby", post_archive_script, "-m", meeting_id)
|
|
|
|
if ret != 0
|
|
|
|
BigBlueButton.logger.warn("Post archive script #{post_archive_script} failed")
|
|
|
|
end
|
|
|
|
end
|
2014-04-25 08:08:06 +08:00
|
|
|
end
|
2014-04-04 04:23:49 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
def post_process(meeting_id)
|
2014-08-12 02:57:32 +08:00
|
|
|
Dir.glob("post_process/*.rb").sort.each do |post_process_script|
|
|
|
|
BigBlueButton.logger.info("Running post process script #{post_process_script}")
|
|
|
|
ret = BigBlueButton.exec_ret("ruby", post_process_script, "-m", meeting_id)
|
|
|
|
if ret != 0
|
|
|
|
BigBlueButton.logger.warn("Post process script #{post_process_script} failed")
|
|
|
|
end
|
|
|
|
end
|
2014-04-25 08:08:06 +08:00
|
|
|
end
|
2014-04-04 04:23:49 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
def post_publish(meeting_id)
|
2014-08-12 02:57:32 +08:00
|
|
|
Dir.glob("post_publish/*.rb").sort.each do |post_publish_script|
|
|
|
|
BigBlueButton.logger.info("Running post publish script #{post_publish_script}")
|
|
|
|
ret = BigBlueButton.exec_ret("ruby", post_publish_script, "-m", meeting_id)
|
|
|
|
if ret != 0
|
|
|
|
BigBlueButton.logger.warn("Post publish script #{post_publish_script} failed")
|
|
|
|
end
|
|
|
|
end
|
2014-04-25 08:08:06 +08:00
|
|
|
end
|
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
begin
|
|
|
|
props = YAML::load(File.open('bigbluebutton.yml'))
|
2014-04-25 08:08:06 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
log_dir = props['log_dir']
|
|
|
|
recording_dir = props['recording_dir']
|
2014-04-04 04:23:49 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
logger = Logger.new("#{log_dir}/bbb-rap-worker.log",'daily' )
|
2014-07-22 03:00:29 +08:00
|
|
|
logger.level = Logger::INFO
|
2014-06-13 05:49:06 +08:00
|
|
|
BigBlueButton.logger = logger
|
2011-05-14 05:45:59 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
BigBlueButton.logger.debug("Running rap-worker...")
|
2014-04-25 08:08:06 +08:00
|
|
|
|
2014-06-13 05:49:06 +08:00
|
|
|
archive_recorded_meeting(recording_dir)
|
|
|
|
sanity_archived_meeting(recording_dir)
|
|
|
|
process_archived_meeting(recording_dir)
|
|
|
|
publish_processed_meeting(recording_dir)
|
2014-07-22 03:00:29 +08:00
|
|
|
|
|
|
|
BigBlueButton.logger.debug("rap-worker done")
|
|
|
|
|
2014-04-25 08:08:06 +08:00
|
|
|
rescue Exception => e
|
2014-06-13 05:49:06 +08:00
|
|
|
BigBlueButton.logger.error(e.message)
|
|
|
|
e.backtrace.each do |traceline|
|
|
|
|
BigBlueButton.logger.error(traceline)
|
|
|
|
end
|
2014-05-06 07:27:26 +08:00
|
|
|
end
|