diff --git a/bigbluebutton-config/bin/bbb-conf b/bigbluebutton-config/bin/bbb-conf index 2c05eb72f6ddaccd2d1803223f36821be2d8503c..15166aba4f4f68e6622ec5bee8ff7de78bd1e1f9 100755 --- a/bigbluebutton-config/bin/bbb-conf +++ b/bigbluebutton-config/bin/bbb-conf @@ -330,7 +330,7 @@ stop_bigbluebutton () { TOMCAT_SERVICE=$TOMCAT_USER fi - systemctl stop $BBB_RED5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-rap-archive-worker.service bbb-rap-process-worker.service bbb-rap-publish-worker.service bbb-rap-sanity-worker.service bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB $BBB_LTI + systemctl stop $BBB_RED5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-rap-resque-worker.service bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB $BBB_LTI } start_bigbluebutton () { @@ -387,7 +387,7 @@ start_bigbluebutton () { fi - systemctl start $BBB_RED5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB $BBB_LTI + systemctl start $BBB_RED5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-rap-resque-worker bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB $BBB_LTI if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then systemctl start mongod @@ -2006,6 +2006,7 @@ if [ $CLEAN ]; then # Clean out the log files for record and playback # rm -f /var/log/bigbluebutton/bbb-rap-worker.log* + rm -f /var/log/bigbluebutton/bbb-rap-resque.log* rm -f /var/log/bigbluebutton/archive.log* if [ -d /var/log/bigbluebutton/html5 ]; then rm -f /var/log/bigbluebutton/html5/* diff --git a/bigbluebutton-config/bin/bbb-record b/bigbluebutton-config/bin/bbb-record index 10d339829b0c83822f0be471ecadb4dde0d88d64..62b8d8a5876b2ebb9b527d4473148f8ee866f1bd 100755 --- a/bigbluebutton-config/bin/bbb-record +++ b/bigbluebutton-config/bin/bbb-record @@ -78,31 +78,34 @@ mark_for_rebuild() { rm -rf /var/bigbluebutton/deleted/$type/$MEETING_ID done - # Restart processing at the 'archived' step - touch $STATUS/archived/$MEETING_ID.done + # touch the file so rap-starter starts the process + touch $STATUS/recorded/$MEETING_ID.done + # In the future it could simply schedule a job on resque + # redis-cli rpush resque:queue:rap:sanity "{\"class\":\"BigBlueButton::Resque::SanityWorker\",\"args\":[{\"meeting_id\":\"$MEETING_ID\"}]}" } mark_for_republish() { MEETING_ID=$1 echo "Marking for republish $MEETING_ID" - if [[ ! -d $BASE/process/$MEETING_ID ]]; then - echo "Processed files for $MEETING_ID do not exist, can't republish" - echo "Try rebuilding the recording instead." - exit 1 - fi - # Clear out the publish done files - rm -vf $STATUS/published/$MEETING_ID* - - # Remove the existing 'published' recording files for type in $TYPES; do + if [[ ! -d $BASE/process/$type/$MEETING_ID ]]; then + echo "Processed files for $MEETING_ID ($type) do not exist, can't republish" + echo "Try rebuilding the recording instead." + continue + fi + + # Clear out the publish done files + rm -vf $STATUS/published/$MEETING_ID* + + # Remove the existing 'published' recording files rm -rf /var/bigbluebutton/published/$type/$MEETING_ID rm -rf /var/bigbluebutton/unpublished/$type/$MEETING_ID rm -rf /var/bigbluebutton/deleted/$type/$MEETING_ID - done - # Restart processing at the 'processed' step - touch $STATUS/processed/$MEETING_ID.done + # Restart processing at the 'publish' step + redis-cli rpush resque:queue:rap:publish "{\"class\":\"BigBlueButton::Resque::PublishWorker\",\"args\":[{\"meeting_id\":\"$MEETING_ID\",\"format_name\":\"$type\"}]}" + done } need_root() { @@ -627,7 +630,7 @@ done echo "--" systemctl --all --no-pager list-timers bbb-record-core.timer echo "--" - systemctl --no-pager status bbb-rap-archive-worker.service bbb-rap-sanity-worker.service bbb-rap-process-worker.service bbb-rap-publish-worker.service + systemctl --no-pager status bbb-rap-starter.service bbb-rap-resque-worker.service echo "--" if tail -n 20 /var/log/bigbluebutton/bbb-web.log | grep -q "is recorded. Process it."; then diff --git a/record-and-playback/core/Gemfile b/record-and-playback/core/Gemfile index 375fc602601ec1320e211cd15e94792a75b502d3..23a4610abeb52e471053c245ecad45933867eccc 100644 --- a/record-and-playback/core/Gemfile +++ b/record-and-playback/core/Gemfile @@ -34,6 +34,7 @@ gem "rb-inotify" gem "redis" gem "rubyzip" gem "trollop", "2.1.3" +gem "resque", "~> 1.27.0" group :test, optional: true do gem "rubocop", "~> 0.71.0" diff --git a/record-and-playback/core/Gemfile.lock b/record-and-playback/core/Gemfile.lock index a73d7fe340ba757ba9c30b14c60bbca372c82b88..9f07f7f88bc66f3452af65c0b6177945b3825d93 100644 --- a/record-and-playback/core/Gemfile.lock +++ b/record-and-playback/core/Gemfile.lock @@ -19,16 +19,30 @@ GEM crass (~> 1.0.2) nokogiri (>= 1.5.9) mini_portile2 (2.4.0) + mono_logger (1.1.0) + multi_json (1.14.1) + mustermann (1.0.3) nokogiri (1.10.4) mini_portile2 (~> 2.4.0) open4 (1.3.4) parallel (1.17.0) parser (2.6.3.0) ast (~> 2.4.0) + rack (2.0.7) + rack-protection (2.0.7) + rack rainbow (3.0.0) rb-inotify (0.10.0) ffi (~> 1.0) redis (4.1.2) + redis-namespace (1.6.0) + redis (>= 3.0.4) + resque (1.27.4) + mono_logger (~> 1.0) + multi_json (~> 1.0) + redis-namespace (~> 1.3) + sinatra (>= 0.9.2) + vegas (~> 0.1.2) rubocop (0.71.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) @@ -38,8 +52,16 @@ GEM unicode-display_width (>= 1.4.0, < 1.7) ruby-progressbar (1.10.1) rubyzip (1.3.0) + sinatra (2.0.7) + mustermann (~> 1.0) + rack (~> 2.0) + rack-protection (= 2.0.7) + tilt (~> 2.0) + tilt (2.0.10) trollop (2.1.3) unicode-display_width (1.6.0) + vegas (0.1.11) + rack (>= 1.0.0) PLATFORMS ruby @@ -58,9 +80,10 @@ DEPENDENCIES open4 rb-inotify redis + resque (~> 1.27.0) rubocop (~> 0.71.0) rubyzip trollop (= 2.1.3) BUNDLED WITH - 2.0.1 + 2.0.2 diff --git a/record-and-playback/core/Rakefile b/record-and-playback/core/Rakefile new file mode 100644 index 0000000000000000000000000000000000000000..5031df6931f3f2a5a764fdb0774cd8250d725d3d --- /dev/null +++ b/record-and-playback/core/Rakefile @@ -0,0 +1,17 @@ +require 'resque/tasks' + +task "resque:setup" => :environment do + props = BigBlueButton.read_props + redis_host = props['redis_workers_host'] || props['redis_host'] + redis_port = props['redis_workers_port'] || props['redis_port'] + Resque.redis = "#{redis_host}:#{redis_port}" + + # Make sure we're in the right directory because several rap scripts assume + # they are being executed from there and will fail otherwise + Dir.chdir(File.expand_path('../scripts', __FILE__)) +end + +task :environment do + require File.expand_path('../lib/recordandplayback', __FILE__) + require File.expand_path('../scripts/workers/workers', __FILE__) +end diff --git a/record-and-playback/core/lib/recordandplayback.rb b/record-and-playback/core/lib/recordandplayback.rb index 04bd6e07ac07a15bc74166bdee5a8cc9126d9796..90cbc8327af3b14aea59a1b57d037e42dfcf7224 100755 --- a/record-and-playback/core/lib/recordandplayback.rb +++ b/record-and-playback/core/lib/recordandplayback.rb @@ -36,6 +36,7 @@ require 'logger' require 'find' require 'rubygems' require 'net/http' +require 'journald/logger' module BigBlueButton class MissingDirectoryException < RuntimeError @@ -82,7 +83,8 @@ module BigBlueButton # @return [Logger] def self.logger return @logger if @logger - logger = Logger.new(STDOUT) + + logger = Journald::Logger.new('bbb-rap') logger.level = Logger::INFO @logger = logger end @@ -238,4 +240,18 @@ module BigBlueButton def self.done_to_timestamp(r) BigBlueButton.record_id_to_timestamp(File.basename(r, ".done")) end + + def self.read_props + return @props if @props + filepath = File.expand_path('../../scripts/bigbluebutton.yml', __FILE__) + @props = YAML::load(File.open(filepath)) + end + + def self.create_redis_publisher + props = BigBlueButton.read_props + redis_host = props['redis_host'] + redis_port = props['redis_port'] + redis_password = props['redis_password'] + BigBlueButton.redis_publisher = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password) + end end diff --git a/record-and-playback/core/lib/recordandplayback/events_archiver.rb b/record-and-playback/core/lib/recordandplayback/events_archiver.rb index a0c8f8bc715ec7fe88de5ae376424bb72aa536f7..5b6516d8461ae52a665df66eabcdddfa0c441025 100755 --- a/record-and-playback/core/lib/recordandplayback/events_archiver.rb +++ b/record-and-playback/core/lib/recordandplayback/events_archiver.rb @@ -227,7 +227,7 @@ module BigBlueButton end def store_events(meeting_id, events_file, break_timestamp) - version = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))["bbb_version"] + version = BigBlueButton.read_props["bbb_version"] if File.exist?(events_file) io = File.open(events_file, 'rb') diff --git a/record-and-playback/core/lib/recordandplayback/generators/events.rb b/record-and-playback/core/lib/recordandplayback/generators/events.rb index cc3888e217b6d425d1a15f3af72e316593dd6a00..f425b7d44ca1d6938050360cd0b8cd64fe691e5d 100755 --- a/record-and-playback/core/lib/recordandplayback/generators/events.rb +++ b/record-and-playback/core/lib/recordandplayback/generators/events.rb @@ -68,13 +68,13 @@ module BigBlueButton # Get the timestamp of the first event. def self.first_event_timestamp(events) first_event = events.at_xpath('/recording/event[position() = 1]') - first_event['timestamp'].to_i + first_event['timestamp'].to_i if first_event && first_event.key?('timestamp') end # Get the timestamp of the last event. def self.last_event_timestamp(events) last_event = events.at_xpath('/recording/event[position() = last()]') - last_event['timestamp'].to_i + last_event['timestamp'].to_i if last_event && last_event.key?('timestamp') end # Determine if the start and stop event matched. diff --git a/record-and-playback/core/scripts/archive/archive.rb b/record-and-playback/core/scripts/archive/archive.rb index 9a130b06cd5c0229378ce41383b9daeed6909c1a..def40fdbb3e46022344efa733daa02d411c1edd6 100755 --- a/record-and-playback/core/scripts/archive/archive.rb +++ b/record-and-playback/core/scripts/archive/archive.rb @@ -17,7 +17,7 @@ # 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 File.expand_path('../../../lib/recordandplayback', __FILE__) require 'logger' require 'trollop' require 'yaml' @@ -156,9 +156,7 @@ Trollop::die :meeting_id, "must be provided" if opts[:meeting_id].nil? meeting_id = opts[:meeting_id] break_timestamp = opts[:break_timestamp] -# This script lives in scripts/archive/steps while bigbluebutton.yml lives in scripts/ -props = YAML::load(File.open('bigbluebutton.yml')) - +props = BigBlueButton.read_props audio_dir = props['raw_audio_src'] recording_dir = props['recording_dir'] raw_archive_dir = "#{recording_dir}/raw" diff --git a/record-and-playback/core/scripts/bigbluebutton.yml b/record-and-playback/core/scripts/bigbluebutton.yml index c5dd4006a34f92f76d7feb35199b9b25652e959d..50d4132b1676fba57e66292f430006fb04e1d2c8 100755 --- a/record-and-playback/core/scripts/bigbluebutton.yml +++ b/record-and-playback/core/scripts/bigbluebutton.yml @@ -19,6 +19,21 @@ redis_port: 6379 # Uncomment and set password if redis require it. # redis_password: changeme +# redis_workers_host: 127.0.0.1 +# redis_workers_port: 6379 + +# Sequence of recording steps. Keys are the current step, values +# are the next step(s). Examples: +# current_step: next_step +# "current_step-format": "next_step-format" +# current_step: +# - next_step +# - another_step-format +steps: + archive: "sanity" + sanity: "captions" + captions: "process:presentation" + "process:presentation": "publish:presentation" # For PRODUCTION log_dir: /var/log/bigbluebutton diff --git a/record-and-playback/core/scripts/post_archive/post_archive.rb.example b/record-and-playback/core/scripts/post_archive/post_archive.rb.example index 5fca2550e79808175d1d33335191b72d2eb2f0d0..e04438ab3c60e2958984a26fa26939fed30f2161 100644 --- a/record-and-playback/core/scripts/post_archive/post_archive.rb.example +++ b/record-and-playback/core/scripts/post_archive/post_archive.rb.example @@ -25,6 +25,7 @@ require File.expand_path('../../../lib/recordandplayback', __FILE__) opts = Trollop::options do opt :meeting_id, "Meeting id to archive", :type => String + opt :format, "Playback format name", :type => String end meeting_id = opts[:meeting_id] diff --git a/record-and-playback/core/scripts/post_process/post_process.rb.example b/record-and-playback/core/scripts/post_process/post_process.rb.example index cb6f8688ab629d38d11f257af5849dc59f7c48d3..b3c1323580af442c45431e7816851ef9a0e89de2 100644 --- a/record-and-playback/core/scripts/post_process/post_process.rb.example +++ b/record-and-playback/core/scripts/post_process/post_process.rb.example @@ -25,6 +25,7 @@ require File.expand_path('../../../lib/recordandplayback', __FILE__) opts = Trollop::options do opt :meeting_id, "Meeting id to archive", :type => String + opt :format, "Playback format name", :type => String end meeting_id = opts[:meeting_id] diff --git a/record-and-playback/core/scripts/post_publish/post_publish.rb.example b/record-and-playback/core/scripts/post_publish/post_publish.rb.example index 15bd247cbd2ae2b4425ef0bf17ed38afe8b426b8..3891cbc4010be618cbc82dc2c6c6431f0c678b83 100644 --- a/record-and-playback/core/scripts/post_publish/post_publish.rb.example +++ b/record-and-playback/core/scripts/post_publish/post_publish.rb.example @@ -25,6 +25,7 @@ require File.expand_path('../../../lib/recordandplayback', __FILE__) opts = Trollop::options do opt :meeting_id, "Meeting id to archive", :type => String + opt :format, "Playback format name", :type => String end meeting_id = opts[:meeting_id] diff --git a/record-and-playback/core/scripts/post_publish/post_publish_recording_ready_callback.rb b/record-and-playback/core/scripts/post_publish/post_publish_recording_ready_callback.rb index 42e178169fb2aecd6223cee60456cb33a480107b..369b5dde8bb99d52a851b069e82fbc341ae374e6 100644 --- a/record-and-playback/core/scripts/post_publish/post_publish_recording_ready_callback.rb +++ b/record-and-playback/core/scripts/post_publish/post_publish_recording_ready_callback.rb @@ -32,6 +32,7 @@ BigBlueButton.logger = logger opts = Trollop::options do opt :meeting_id, "Meeting id to archive", :type => String + opt :format, "Playback format name", :type => String end meeting_id = opts[:meeting_id] diff --git a/record-and-playback/core/scripts/rap-archive-worker.rb b/record-and-playback/core/scripts/rap-archive-worker.rb deleted file mode 100755 index 138d35e287a4b7c2b7a64d8e6c74dcb23d333f60..0000000000000000000000000000000000000000 --- a/record-and-playback/core/scripts/rap-archive-worker.rb +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/ruby -# encoding: UTF-8 - -# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. -# -# This file is part of BigBlueButton open source conferencing system. -# -# BigBlueButton 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 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_meetings(recording_dir) - recorded_done_files = Dir.glob("#{recording_dir}/status/recorded/*.done") - - FileUtils.mkdir_p("#{recording_dir}/status/archived") - recorded_done_files.each do |recorded_done| - recorded_done_base = File.basename(recorded_done, '.done') - meeting_id = nil - break_timestamp = nil - - if match = /^([0-9a-f]+-[0-9]+)$/.match(recorded_done_base) - meeting_id = match[1] - elsif match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(recorded_done_base) - meeting_id = match[1] - break_timestamp = match[2] - else - BigBlueButton.logger.warn("Recording done file for #{recorded_done_base} has invalid format") - next - end - - archived_done = "#{recording_dir}/status/archived/#{recorded_done_base}.done" - next if File.exists?(archived_done) - - archived_norecord = "#{recording_dir}/status/archived/#{recorded_done_base}.norecord" - next if File.exists?(archived_norecord) - - # The fail filename doesn't contain the break timestamp, because we need an - # archive failure to block archiving of future segments. - archived_fail = "#{recording_dir}/status/archived/#{meeting_id}.fail" - next if File.exists?(archived_fail) - - # TODO: define redis messages for recording segments... - BigBlueButton.redis_publisher.put_archive_started(meeting_id) - - step_start_time = BigBlueButton.monotonic_clock - if !break_timestamp.nil? - ret = BigBlueButton.exec_ret("ruby", "archive/archive.rb", "-m", meeting_id, '-b', break_timestamp) - else - ret = BigBlueButton.exec_ret("ruby", "archive/archive.rb", "-m", meeting_id) - end - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - - step_succeeded = (ret == 0 && - (File.exists?(archived_done) || - File.exists?(archived_norecord))) - - BigBlueButton.redis_publisher.put_archive_ended(meeting_id, { - "success" => step_succeeded, - "step_time" => step_time - }) - - if step_succeeded - BigBlueButton.logger.info("Successfully archived #{recorded_done_base}") - FileUtils.rm_f(recorded_done) - else - BigBlueButton.logger.error("Failed to archive #{recorded_done_base}") - FileUtils.touch(archived_fail) - end - end -end - -begin - props = YAML::load(File.open('bigbluebutton.yml')) - redis_host = props['redis_host'] - redis_port = props['redis_port'] - redis_password = props['redis_password'] - BigBlueButton.redis_publisher = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password) - - log_dir = props['log_dir'] - recording_dir = props['recording_dir'] - - logger = Logger.new("#{log_dir}/bbb-rap-worker.log") - logger.level = Logger::INFO - BigBlueButton.logger = logger - - BigBlueButton.logger.debug("Running rap-archive-worker...") - - archive_recorded_meetings(recording_dir) - - BigBlueButton.logger.debug("rap-archive-worker done") - -rescue Exception => e - BigBlueButton.logger.error(e.message) - e.backtrace.each do |traceline| - BigBlueButton.logger.error(traceline) - end -end diff --git a/record-and-playback/core/scripts/rap-events-worker.rb b/record-and-playback/core/scripts/rap-events-worker.rb index 6cfff5d6d4101cf192b6607ed718c99eb6fb1925..2c259934285d80921f0a6b6ba78e2a64d843fef3 100755 --- a/record-and-playback/core/scripts/rap-events-worker.rb +++ b/record-and-playback/core/scripts/rap-events-worker.rb @@ -1,5 +1,5 @@ #!/usr/bin/ruby -# encoding: UTF-8 +# encoding: utf-8 # Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. # @@ -24,18 +24,18 @@ require 'yaml' require 'fileutils' def run_post_events_script(meeting_id) - Dir.glob("post_events/*.rb").sort.each do |post_event_script| - match = /([^\/]*).rb$/.match(post_event_script) + Dir.glob('post_events/*.rb').sort.each do |post_event_script| + match = %r{([^/]*).rb$}.match(post_event_script) post_type = match[1] BigBlueButton.logger.info("Running post event script #{post_type}") - step_start_time = BigBlueButton.monotonic_clock - ret = BigBlueButton.exec_ret("ruby", post_event_script, "-m", meeting_id) - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - step_succeeded = (ret == 0) + # step_start_time = BigBlueButton.monotonic_clock + ret = BigBlueButton.exec_ret('ruby', post_event_script, '-m', meeting_id) + # step_stop_time = BigBlueButton.monotonic_clock + # step_time = step_stop_time - step_start_time + step_succeeded = ret.zero? - if not step_succeeded + unless step_succeeded BigBlueButton.logger.warn("Post event script #{post_event_script} failed") end end @@ -45,7 +45,7 @@ def keep_meeting_events(recording_dir, ended_done_file) ended_done_base = File.basename(ended_done_file, '.done') meeting_id = nil break_timestamp = nil - + if match = /^([0-9a-f]+-[0-9]+)$/.match(ended_done_base) meeting_id = match[1] elsif match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(ended_done_base) @@ -55,21 +55,20 @@ def keep_meeting_events(recording_dir, ended_done_file) BigBlueButton.logger.warn("Ended done file for #{ended_done_base} has invalid format") end - if meeting_id != nil - if !break_timestamp.nil? - ret = BigBlueButton.exec_ret("ruby", "events/events.rb", "-m", meeting_id, '-b', break_timestamp) - else - ret = BigBlueButton.exec_ret("ruby", "events/events.rb", "-m", meeting_id) + return if meeting_id.nil? - # Need to only run post events scripts after meeting ends not during - # segments. - run_post_events_script(meeting_id) - end + if !break_timestamp.nil? + BigBlueButton.exec_ret('ruby', 'events/events.rb', '-m', meeting_id, '-b', break_timestamp) + else + BigBlueButton.exec_ret('ruby', 'events/events.rb', '-m', meeting_id) + + # Need to only run post events scripts after meeting ends not during segments. + run_post_events_script(meeting_id) end end begin - props = YAML::load(File.open('bigbluebutton.yml')) + props = YAML.safe_load(File.open('bigbluebutton.yml')) redis_host = props['redis_host'] redis_port = props['redis_port'] redis_password = props['redis_password'] @@ -77,25 +76,22 @@ begin log_dir = props['log_dir'] recording_dir = props['recording_dir'] - raw_archive_dir = "#{recording_dir}/raw" - events_dir = props['events_dir'] logger = Logger.new("#{log_dir}/bbb-rap-worker.log") - #logger = Logger.new(STDOUT) + # logger = Logger.new(STDOUT) logger.level = Logger::INFO BigBlueButton.logger = logger - BigBlueButton.logger.info("Running rap-events-worker...") + BigBlueButton.logger.info('Running rap-events-worker...') ended_done_files = Dir.glob("#{recording_dir}/status/ended/*.done") ended_done_files.each do |ended_done| - ended_done_base = File.basename(ended_done, '.done') keep_meeting_events(recording_dir, ended_done) end - BigBlueButton.logger.debug("rap-events-worker done") + BigBlueButton.logger.debug('rap-events-worker done') rescue Exception => e BigBlueButton.logger.error(e.message) e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) end -end \ No newline at end of file +end diff --git a/record-and-playback/core/scripts/rap-process-worker.rb b/record-and-playback/core/scripts/rap-process-worker.rb deleted file mode 100755 index 535ec6c5bd92c54b508c75fa46cc2c98cac5b7b9..0000000000000000000000000000000000000000 --- a/record-and-playback/core/scripts/rap-process-worker.rb +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/ruby -# encoding: UTF-8 - -# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. -# -# This file is part of BigBlueButton open source conferencing system. -# -# BigBlueButton 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 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' -require 'optparse' - -def process_archived_meetings(recording_dir, pattern) - sanity_done_files = Dir.glob("#{recording_dir}/status/sanity/*.done") - - FileUtils.mkdir_p("#{recording_dir}/status/processed") - # TODO sort by timestamp(s) - sanity_done_files.each do |sanity_done| - done_base = File.basename(sanity_done, '.done') - meeting_id = nil - break_timestamp = nil - - if match = /^([0-9a-f]+-[0-9]+)$/.match(done_base) - meeting_id = match[1] - elsif match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(done_base) - meeting_id = match[1] - break_timestamp = match[2] - else - BigBlueButton.logger.warn("Sanity done file for #{done_base} has invalid format") - next - end - - unless pattern.nil? - next unless pattern.match(done_base) - - BigBlueButton.logger.info("Processing #{done_base} because it matched the pattern #{pattern.inspect}") - end - - step_succeeded = true - - # Generate captions - ret = BigBlueButton.exec_ret('ruby', 'utils/captions.rb', '-m', meeting_id) - if ret != 0 - BigBlueButton.logger.warn("Failed to generate caption files #{ret}") - end - - # 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/#{done_base}-#{process_type}.done" - next if File.exists?(processed_done) - - processed_fail = "#{recording_dir}/status/processed/#{done_base}-#{process_type}.fail" - if File.exists?(processed_fail) - step_succeeded = false - next - end - - BigBlueButton.redis_publisher.put_process_started(process_type, meeting_id) - - # If the process directory exists, the script does nothing - process_dir = "#{recording_dir}/process/#{process_type}/#{done_base}" - FileUtils.rm_rf(process_dir) - - step_start_time = BigBlueButton.monotonic_clock - if !break_timestamp.nil? - ret = BigBlueButton.exec_ret('ruby', process_script, - '-m', meeting_id, '-b', break_timestamp) - else - ret = BigBlueButton.exec_ret('ruby', process_script, '-m', meeting_id) - end - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - - if File.directory?(process_dir) - IO.write("#{process_dir}/processing_time", step_time) - end - - step_succeeded = (ret == 0 and File.exists?(processed_done)) - - BigBlueButton.redis_publisher.put_process_ended(process_type, meeting_id, { - "success" => step_succeeded, - "step_time" => step_time - }) - - if step_succeeded - BigBlueButton.logger.info("Process format #{process_type} succeeded for #{meeting_id} break #{break_timestamp}") - BigBlueButton.logger.info("Process took #{step_time}ms") - else - BigBlueButton.logger.info("Process format #{process_type} failed for #{meeting_id} break #{break_timestamp}") - BigBlueButton.logger.info("Process took #{step_time}ms") - FileUtils.touch(processed_fail) - step_succeeded = false - end - end - - if step_succeeded - post_process(meeting_id) - FileUtils.rm_f(sanity_done) - end - end -end - -def post_process(meeting_id) - Dir.glob("post_process/*.rb").sort.each do |post_process_script| - match = /([^\/]*).rb$/.match(post_process_script) - post_type = match[1] - BigBlueButton.logger.info("Running post process script #{post_type}") - - BigBlueButton.redis_publisher.put_post_process_started post_type, meeting_id - - step_start_time = BigBlueButton.monotonic_clock - ret = BigBlueButton.exec_ret("ruby", post_process_script, "-m", meeting_id) - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - step_succeeded = (ret == 0) - - BigBlueButton.redis_publisher.put_post_process_ended post_type, meeting_id, { - "success" => step_succeeded, - "step_time" => step_time - } - - if not step_succeeded - BigBlueButton.logger.warn("Post process script #{post_process_script} failed") - end - end -end - -begin - props = YAML::load(File.open('bigbluebutton.yml')) - redis_host = props['redis_host'] - redis_port = props['redis_port'] - redis_password = props['redis_password'] - BigBlueButton.redis_publisher = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password) - - log_dir = props['log_dir'] - recording_dir = props['recording_dir'] - - logger = Logger.new("#{log_dir}/bbb-rap-worker.log") - logger.level = Logger::INFO - BigBlueButton.logger = logger - - options = { pattern: nil } - OptionParser.new do |opt| - opt.on('-p PATTERN') { |o| options[:pattern] = o } - end.parse! - pattern = Regexp.new(options[:pattern]) unless options[:pattern].nil? - - if pattern.nil? - BigBlueButton.logger.debug("Running rap-process-worker...") - else - BigBlueButton.logger.debug("Running rap-process-worker with pattern #{pattern.inspect}...") - end - process_archived_meetings(recording_dir, pattern) - - BigBlueButton.logger.debug("rap-process-worker done") - -rescue Exception => e - BigBlueButton.logger.error(e.message) - e.backtrace.each do |traceline| - BigBlueButton.logger.error(traceline) - end -end diff --git a/record-and-playback/core/scripts/rap-publish-worker.rb b/record-and-playback/core/scripts/rap-publish-worker.rb deleted file mode 100755 index 69ebdb5d1c9ac51befeb45c67a327553d1246f7e..0000000000000000000000000000000000000000 --- a/record-and-playback/core/scripts/rap-publish-worker.rb +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/ruby -# encoding: UTF-8 - -# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. -# -# This file is part of BigBlueButton open source conferencing system. -# -# BigBlueButton 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 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' -require 'custom_hash' - -def publish_processed_meetings(recording_dir) - processed_done_files = Dir.glob("#{recording_dir}/status/processed/*.done") - - FileUtils.mkdir_p("#{recording_dir}/status/published") - # TODO sort by timestamp(s) - processed_done_files.each do |processed_done| - done_base = File.basename(processed_done, '.done') - meeting_id = nil - break_timestamp = nil - publish_type = nil - - if match = /^([0-9a-f]+-[0-9]+)-([0-9]+)-(.+)$/.match(done_base) - meeting_id = match[1] - break_timestamp = match[2] - publish_type = match[3] - elsif match = /^([0-9a-f]+-[0-9]+)-(.+)$/.match(done_base) - meeting_id = match[1] - publish_type = match[2] - else - BigBlueButton.logger.warn("Processed done file for #{done_base} has invalid format") - next - end - if !break_timestamp.nil? - done_base = "#{meeting_id}-#{break_timestamp}" - else - done_base = meeting_id - end - - step_succeeded = false - - published_done = "#{recording_dir}/status/published/#{done_base}-#{publish_type}.done" - next if File.exists?(published_done) - - published_fail = "#{recording_dir}/status/published/#{done_base}-#{publish_type}.fail" - next if File.exists?(published_fail) - - publish_script = "publish/#{publish_type}.rb" - if File.exists?(publish_script) - BigBlueButton.redis_publisher.put_publish_started(publish_type, meeting_id) - - # If the publish directory exists, the script does nothing - process_dir = "#{recording_dir}/process/#{publish_type}/#{done_base}" - publish_dir = "#{recording_dir}/publish/#{publish_type}/#{done_base}" - FileUtils.rm_rf(publish_dir) - - step_start_time = BigBlueButton.monotonic_clock - # For legacy reasons, the meeting ID passed to the publish script contains - # the playback format name. - if !break_timestamp.nil? - ret = BigBlueButton.exec_ret('ruby', publish_script, - '-m', "#{meeting_id}-#{publish_type}", - '-b', break_timestamp) - else - ret = BigBlueButton.exec_ret('ruby', publish_script, - '-m', "#{meeting_id}-#{publish_type}") - end - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - - step_succeeded = (ret == 0 and File.exists?(published_done)) - - props = YAML::load(File.open('bigbluebutton.yml')) - published_dir = props['published_dir'] - - playback = {} - metadata = {} - download = {} - raw_size = {} - start_time = {} - end_time = {} - metadata_xml_path = "#{published_dir}/#{publish_type}/#{done_base}/metadata.xml" - if File.exists? metadata_xml_path - begin - doc = Hash.from_xml(File.open(metadata_xml_path)) - playback = doc[:recording][:playback] if !doc[:recording][:playback].nil? - metadata = doc[:recording][:meta] if !doc[:recording][:meta].nil? - download = doc[:recording][:download] if !doc[:recording][:download].nil? - raw_size = doc[:recording][:raw_size] if !doc[:recording][:raw_size].nil? - start_time = doc[:recording][:start_time] if !doc[:recording][:start_time].nil? - end_time = doc[:recording][:end_time] if !doc[:recording][:end_time].nil? - rescue Exception => e - BigBlueButton.logger.warn "An exception occurred while loading the extra information for the publish event" - BigBlueButton.logger.warn e.message - e.backtrace.each do |traceline| - BigBlueButton.logger.warn traceline - end - end - else - BigBlueButton.logger.warn "Couldn't find the metadata file at #{metadata_xml_path}" - end - - BigBlueButton.redis_publisher.put_publish_ended(publish_type, meeting_id, { - "success" => step_succeeded, - "step_time" => step_time, - "playback" => playback, - "metadata" => metadata, - "download" => download, - "raw_size" => raw_size, - "start_time" => start_time, - "end_time" => end_time - }) - else - BigBlueButton.logger.warn("Processed recording found for type #{publish_type}, but no publish script exists") - step_succeeded = true - end - - if step_succeeded - BigBlueButton.logger.info("Publish format #{publish_type} succeeded for #{meeting_id} break #{break_timestamp}") - FileUtils.rm_f(processed_done) - FileUtils.rm_rf(process_dir) - FileUtils.rm_rf(publish_dir) - - # Check if this is the last format to be published - if Dir.glob("#{recording_dir}/status/processed/#{meeting_id}-*.done").length == 0 - post_publish(meeting_id) - end - else - BigBlueButton.logger.info("Publish format #{publish_type} failed for #{meeting_id} break #{break_timestamp}") - FileUtils.touch(published_fail) - end - - end -end - -def post_publish(meeting_id) - Dir.glob("post_publish/*.rb").sort.each do |post_publish_script| - match = /([^\/]*).rb$/.match(post_publish_script) - post_type = match[1] - BigBlueButton.logger.info("Running post publish script #{post_type}") - - BigBlueButton.redis_publisher.put_post_publish_started post_type, meeting_id - - step_start_time = BigBlueButton.monotonic_clock - ret = BigBlueButton.exec_ret("ruby", post_publish_script, "-m", meeting_id) - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - step_succeeded = (ret == 0) - - BigBlueButton.redis_publisher.put_post_publish_ended post_type, meeting_id, { - "success" => step_succeeded, - "step_time" => step_time - } - - if not step_succeeded - BigBlueButton.logger.warn("Post publish script #{post_publish_script} failed") - end - end -end - -begin - props = YAML::load(File.open('bigbluebutton.yml')) - redis_host = props['redis_host'] - redis_port = props['redis_port'] - redis_password = props['redis_password'] - BigBlueButton.redis_publisher = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password) - - log_dir = props['log_dir'] - recording_dir = props['recording_dir'] - - logger = Logger.new("#{log_dir}/bbb-rap-worker.log") - logger.level = Logger::INFO - BigBlueButton.logger = logger - - BigBlueButton.logger.debug("Running rap-publish-worker...") - - publish_processed_meetings(recording_dir) - - BigBlueButton.logger.debug("rap-publish-worker done") - -rescue Exception => e - BigBlueButton.logger.error(e.message) - e.backtrace.each do |traceline| - BigBlueButton.logger.error(traceline) - end -end - diff --git a/record-and-playback/core/scripts/rap-sanity-worker.rb b/record-and-playback/core/scripts/rap-sanity-worker.rb deleted file mode 100755 index a54eb12b73e68f6643471b710669415631a0719e..0000000000000000000000000000000000000000 --- a/record-and-playback/core/scripts/rap-sanity-worker.rb +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/ruby -# encoding: UTF-8 - -# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. -# -# This file is part of BigBlueButton open source conferencing system. -# -# BigBlueButton 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 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 sanity_archived_meetings(recording_dir) - archived_done_files = Dir.glob("#{recording_dir}/status/archived/*.done") - - FileUtils.mkdir_p("#{recording_dir}/status/sanity") - archived_done_files.each do |archived_done| - archived_done_base = File.basename(archived_done, '.done') - meeting_id = nil - break_timestamp = nil - - if match = /^([0-9a-f]+-[0-9]+)$/.match(archived_done_base) - meeting_id = match[1] - elsif match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(archived_done_base) - meeting_id = match[1] - break_timestamp = match[2] - else - BigBlueButton.logger.warn("Archive done file for #{archived_done_base} has invalid format") - next - end - - sanity_done = "#{recording_dir}/status/sanity/#{archived_done_base}.done" - next if File.exists?(sanity_done) - - sanity_fail = "#{recording_dir}/status/sanity/#{archived_done_base}.fail" - next if File.exists?(sanity_fail) - - # TODO: define redis messages for recording segments... - BigBlueButton.redis_publisher.put_sanity_started(meeting_id) - - step_start_time = BigBlueButton.monotonic_clock - if !break_timestamp.nil? - ret = BigBlueButton.exec_ret('ruby', 'sanity/sanity.rb', - '-m', meeting_id, '-b', break_timestamp) - else - ret = BigBlueButton.exec_ret('ruby', 'sanity/sanity.rb', '-m', meeting_id) - end - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - - step_succeeded = (ret == 0 && File.exists?(sanity_done)) - - BigBlueButton.redis_publisher.put_sanity_ended(meeting_id, { - "success" => step_succeeded, - "step_time" => step_time - }) - - if step_succeeded - BigBlueButton.logger.info("Successfully sanity checked #{meeting_id}") - post_archive(meeting_id) - FileUtils.rm_f(archived_done) - else - BigBlueButton.logger.error("Sanity check failed on #{meeting_id}") - FileUtils.touch(sanity_fail) - end - end -end - -def post_archive(meeting_id) - Dir.glob("post_archive/*.rb").sort.each do |post_archive_script| - match = /([^\/]*).rb$/.match(post_archive_script) - post_type = match[1] - BigBlueButton.logger.info("Running post archive script #{post_type}") - - BigBlueButton.redis_publisher.put_post_archive_started(post_type, meeting_id) - - step_start_time = BigBlueButton.monotonic_clock - ret = BigBlueButton.exec_ret("ruby", post_archive_script, "-m", meeting_id) - step_stop_time = BigBlueButton.monotonic_clock - step_time = step_stop_time - step_start_time - step_succeeded = (ret == 0) - - BigBlueButton.redis_publisher.put_post_archive_ended(post_type, meeting_id, { - "success" => step_succeeded, - "step_time" => step_time - }) - - if not step_succeeded - BigBlueButton.logger.warn("Post archive script #{post_archive_script} failed") - end - end -end - -begin - props = YAML::load(File.open('bigbluebutton.yml')) - redis_host = props['redis_host'] - redis_port = props['redis_port'] - redis_password = props['redis_password'] - BigBlueButton.redis_publisher = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password) - - log_dir = props['log_dir'] - recording_dir = props['recording_dir'] - - logger = Logger.new("#{log_dir}/bbb-rap-worker.log") - logger.level = Logger::INFO - BigBlueButton.logger = logger - - BigBlueButton.logger.debug("Running rap-sanity-worker...") - - sanity_archived_meetings(recording_dir) - - BigBlueButton.logger.debug("rap-sanity-worker done") - -rescue Exception => e - BigBlueButton.logger.error(e.message) - e.backtrace.each do |traceline| - BigBlueButton.logger.error(traceline) - end -end diff --git a/record-and-playback/core/scripts/rap-starter.rb b/record-and-playback/core/scripts/rap-starter.rb new file mode 100755 index 0000000000000000000000000000000000000000..5b97b68bbfef115331caac475e59dac4762ece10 --- /dev/null +++ b/record-and-playback/core/scripts/rap-starter.rb @@ -0,0 +1,100 @@ +#!/usr/bin/ruby +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../../lib/recordandplayback', __FILE__) +require File.expand_path('../workers/workers', __FILE__) +require 'rubygems' +require 'yaml' +require 'fileutils' +require 'resque' +require 'rb-inotify' + +class StopSignalException < StandardError +end + +def archive_recorded_meetings(recording_dir, done_file) + FileUtils.mkdir_p("#{recording_dir}/status/archived") + meeting_id = nil + break_timestamp = nil + + if match = /^([0-9a-f]+-[0-9]+)$/.match(done_file) + meeting_id = match[1] + elsif match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(done_file) + meeting_id = match[1] + break_timestamp = match[2] + else + BigBlueButton.logger.warn("Recording done file for #{done_file} has invalid format") + return + end + + attrs = { + 'meeting_id': meeting_id, + 'break_timestamp': break_timestamp, + } + BigBlueButton.logger.info("Enqueuing job to archive #{attrs.inspect}") + Resque.enqueue(BigBlueButton::Resque::ArchiveWorker, attrs) +end + +begin + props = BigBlueButton.read_props + log_dir = props['log_dir'] + + redis_host = props['redis_workers_host'] || props['redis_host'] + redis_port = props['redis_workers_port'] || props['redis_port'] + Resque.redis = "#{redis_host}:#{redis_port}" + + BigBlueButton.logger.debug('Running rap-trigger...') + + recording_dir = props['recording_dir'] + watch_dir = "#{recording_dir}/status/recorded/" + + # start the process for all .done files already there + done_files = Dir.glob("#{watch_dir}*.done") + done_files.each do |file| + id = File.basename(file, '.done') + BigBlueButton.logger.info "Detected recording #{id}, starting the processing" + archive_recorded_meetings(recording_dir, id) + FileUtils.rm_f(file) + end + + # Listen the directory for when new files are created + BigBlueButton.logger.info("Setting up inotify watch on #{watch_dir}") + notifier = INotify::Notifier.new + notifier.watch(watch_dir, :moved_to, :create) do |event| + next unless event.name.end_with?('.done') + + id = File.basename(event.name, '.done') + BigBlueButton.logger.info "Detected recording #{id}, starting the processing" + archive_recorded_meetings(recording_dir, id) + FileUtils.rm_f(event.absolute_name) + end + + BigBlueButton.logger.info('Waiting for new recordings...') + Signal.trap('INT') { raise StopSignalException.new } + Signal.trap('TERM') { raise StopSignalException.new } + notifier.run +rescue StopSignalException + notifier.stop +rescue Exception => e + BigBlueButton.logger.error(e.message) + e.backtrace.each do |traceline| + BigBlueButton.logger.error(traceline) + end +end diff --git a/record-and-playback/core/scripts/sanity/sanity.rb b/record-and-playback/core/scripts/sanity/sanity.rb index a012f790bfd5f3ceaa62ea2056711660b6769b7a..4ded97524d1388f8c53add3489ec876f79fcd3e3 100755 --- a/record-and-playback/core/scripts/sanity/sanity.rb +++ b/record-and-playback/core/scripts/sanity/sanity.rb @@ -18,8 +18,7 @@ # with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # - -require '../lib/recordandplayback' +require File.expand_path('../../../lib/recordandplayback', __FILE__) require 'logger' require 'trollop' require 'yaml' @@ -27,8 +26,7 @@ require "nokogiri" require "redis" require "fileutils" -# This script lives in scripts/archive/steps while bigbluebutton.yml lives in scripts/ -props = YAML::load(File.open('bigbluebutton.yml')) +props = BigBlueButton.read_props log_dir = props['log_dir'] audio_dir = props['raw_audio_src'] recording_dir = props['recording_dir'] diff --git a/record-and-playback/core/scripts/workers/rap-archive-worker.rb b/record-and-playback/core/scripts/workers/rap-archive-worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..b85999d14ca5b550f5a8aed8727841d5c4a52da8 --- /dev/null +++ b/record-and-playback/core/scripts/workers/rap-archive-worker.rb @@ -0,0 +1,81 @@ +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../workers', __FILE__) + +module BigBlueButton + module Resque + class ArchiveWorker < BaseWorker + @queue = 'rap:archive' + + def perform + super do + @logger.info("Running archive worker for #{@full_id}") + @publisher.put_archive_started(@meeting_id) + + remove_status_files + + script = File.expand_path('../../archive/archive.rb', __FILE__) + if @break_timestamp.nil? + ret, step_time = run_script(script, '-m', @meeting_id) + else + ret, step_time = run_script(script, '-m', @meeting_id, '-b', @break_timestamp) + end + + step_succeeded = ( + ret.zero? && + (File.exist?(@archived_done) || File.exist?(@archived_norecord)) && + !File.exist?(@archived_fail) + ) + + @publisher.put_archive_ended( + @meeting_id, { + success: step_succeeded, + step_time: step_time, + }) + + if step_succeeded + @logger.info("Successfully archived #{@full_id}") + else + @logger.error("Failed to archive #{@full_id}") + FileUtils.touch(@archived_fail) + end + @logger.debug("Finished archive worker for #{@full_id}") + + step_succeeded + end + end + + def remove_status_files + FileUtils.rm_f(@archived_done) + FileUtils.rm_f(@archived_norecord) + FileUtils.rm_f(@archived_fail) + end + + def initialize(opts) + super(opts) + @step_name = 'archive' + @archived_fail = "#{@recording_dir}/status/archived/#{@meeting_id}.fail" + @archived_done = "#{@recording_dir}/status/archived/#{@meeting_id}.done" + @archived_norecord = "#{@recording_dir}/status/archived/#{@meeting_id}.norecord" + end + end + end +end + diff --git a/record-and-playback/core/scripts/workers/rap-base-worker.rb b/record-and-playback/core/scripts/workers/rap-base-worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..23ad6c705914dc86a7fdb7fdd7fff43fc26a1cfe --- /dev/null +++ b/record-and-playback/core/scripts/workers/rap-base-worker.rb @@ -0,0 +1,173 @@ +#!/usr/bin/ruby +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../../../lib/recordandplayback', __FILE__) +require File.expand_path('../workers', __FILE__) +require 'rubygems' +require 'yaml' +require 'fileutils' +require 'resque' + +module BigBlueButton + module Resque + class BaseWorker + @queue = 'rap:base' + + def self.perform(*args) + worker = new(*args) + worker.perform + + # remove all workers that are not working anymore, + # a simple form of garbage collection + ::Resque.workers.first.prune_dead_workers + end + + def perform + @logger.info("Started worker #{@step_name} for #{@meeting_id}") + success = yield + @logger.info("Ended worker #{@step_name} for #{@meeting_id} with result #{success}") + + if success + schedule_next_step unless @single_step + else + raise "Worker #{@step_name} for #{@meeting_id} failed with result #{success}" + end + rescue Exception => e + @logger.error(e.message) + e.backtrace.each do |traceline| + @logger.error(traceline) + end + raise e # so that resque knows the job failed + end + + def run_post_scripts(post_scripts_path) + glob = File.join(post_scripts_path, '*.rb') + Dir.glob(glob).sort.each do |post_script| + match = %r{([^/]*).rb$}.match(post_script) + post_type = match[1] + @logger.info("Running post #{@step_name} script #{post_type}") + + post_started_method(post_type, @meeting_id) + + if @format_name.nil? + ret, step_time = run_script(post_script, '-m', @meeting_id) + else + ret, step_time = run_script(post_script, '-m', @meeting_id, '-f', @format_name) + end + step_succeeded = ret.zero? + + post_ended_method( + post_type, @meeting_id, { + success: step_succeeded, + step_time: step_time, + }) + + unless step_succeeded + @logger.warn("Post #{@step_name} script #{post_script}/#{post_type} failed") + end + end + end + + def run_script(script, *args) + step_start_time = BigBlueButton.monotonic_clock + ret = BigBlueButton.exec_ret('ruby', script, *args) + step_stop_time = BigBlueButton.monotonic_clock + step_time = step_stop_time - step_start_time + [ret, step_time] + end + + def post_started_method(*args) + case @step_name + when 'archive' + @publisher.put_post_archive_started(*args) + when 'process' + @publisher.put_post_process_started(*args) + when 'publish' + @publisher.put_post_publish_started(*args) + end + end + + def post_ended_method(*args) + case @step_name + when 'archive' + @publisher.put_post_archive_ended(*args) + when 'process' + @publisher.put_post_process_ended(*args) + when 'publish' + @publisher.put_post_publish_ended(*args) + end + end + + def schedule_next_step + @logger.info("Scheduling next step for #{@step_name}") + + opts = { + 'meeting_id': @meeting_id, + 'single_step': false, + } + + # get the steps from the properties files + props = BigBlueButton.read_props + + next_step = props['steps']["#{@step_name}:#{@format_name}"] + next_step = props['steps'][@step_name] if next_step.nil? + + # make it always an array e.g. [ "process:presentation" ] + next_step = [next_step] if next_step && !next_step.is_a?(Array) + + if next_step.nil? + @logger.info("No next step for #{@step_name}, will not schedule anything") + else + next_step.each do |step| + step_name, step_format = step.split(':') # e.g. 'process:presentation' + opts['format_name'] = step_format unless step_format.nil? + + @logger.info("Enqueueing #{step_name} worker with #{opts.inspect}") + klass = Object.const_get("BigBlueButton::Resque::#{step_name.capitalize}Worker") + ::Resque.enqueue(klass, opts) + end + end + end + + def initialize(opts) + props = BigBlueButton.read_props + BigBlueButton.create_redis_publisher + + @publisher = BigBlueButton.redis_publisher + @log_dir = props['log_dir'] + @recording_dir = props['recording_dir'] + @meeting_id = opts['meeting_id'] + @break_timestamp = opts['break_timestamp'] + @single_step = opts['single_step'] || false + @step_name = nil + @format_name = nil + @full_id = if @break_timestamp.nil? + @meeting_id + else + "#{@meeting_id}-#{break_timestamp}" + end + + @logger = BigBlueButton.logger + ::Resque.logger = @logger + ::Resque.logger.level = Logger::INFO + end + end + end +end diff --git a/record-and-playback/core/scripts/workers/rap-captions-worker.rb b/record-and-playback/core/scripts/workers/rap-captions-worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..817d23507391ea8ba909d66f117fb4c56c7ce5af --- /dev/null +++ b/record-and-playback/core/scripts/workers/rap-captions-worker.rb @@ -0,0 +1,50 @@ +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../workers', __FILE__) + +module BigBlueButton + module Resque + class CaptionsWorker < BaseWorker + @queue = 'rap:captions' + + def perform + super do + @logger.info("Running captions worker for #{@full_id}") + + ret, = run_script('utils/captions.rb', '-m', @meeting_id) + + if ret.zero? + @logger.info("Succeeded generating captions for #{@full_id}") + else + @logger.error("Failed generating captions for #{@full_id} (got #{ret})") + end + + @logger.info("Finished format succeeded for #{@full_id}") + ret + end + end + + def initialize(opts) + super(opts) + @step_name = 'captions' + end + end + end +end diff --git a/record-and-playback/core/scripts/workers/rap-process-worker.rb b/record-and-playback/core/scripts/workers/rap-process-worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..86944ec66cc0d0249084a448c440d1d47b936872 --- /dev/null +++ b/record-and-playback/core/scripts/workers/rap-process-worker.rb @@ -0,0 +1,95 @@ +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../workers', __FILE__) + +module BigBlueButton + module Resque + class ProcessWorker < BaseWorker + @queue = 'rap:process' + + def perform + super do + @logger.info("Running process worker for #{@full_id}:#{@format_name}") + + script = File.expand_path("../../process/#{@format_name}.rb", __FILE__) + if File.exist?(script) + remove_status_files + + @publisher.put_process_started(@format_name, @meeting_id) + + # If the process directory exists, the script does nothing + FileUtils.rm_rf("#{@recording_dir}/process/#{@format_name}/#{@full_id}") + + if @break_timestamp.nil? + ret, step_time = run_script(script, '-m', @meeting_id) + else + ret, step_time = run_script(script, '-m', @meeting_id, '-b', @break_timestamp) + end + + step_succeeded = ( + ret.zero? && + File.exist?(@processed_done) && !File.exist?(@processed_fail) + ) + + @publisher.put_process_ended( + @format_name, @meeting_id, { + success: step_succeeded, + step_time: step_time, + }) + + if step_succeeded + @logger.info("Process format succeeded for #{@full_id}:#{@format_name}") + + FileUtils.mkdir_p("#{@recording_dir}/process/#{@format_name}/#{@full_id}") + IO.write("#{@recording_dir}/process/#{@format_name}/#{@full_id}/processing_time", step_time) + + run_post_scripts(@post_scripts_path) + else + @logger.error("Process format failed for #{@full_id}:#{@format_name}") + FileUtils.touch(@processed_fail) + end + @logger.info("Process took #{step_time}ms") + + else + @logger.warn("Processed recording found for #{@full_id}:#{@format_name}, but no process script exists") + step_succeeded = true + end + + step_succeeded + end + end + + def remove_status_files + FileUtils.rm_f(@processed_done) + FileUtils.rm_f(@processed_fail) + end + + def initialize(opts) + super(opts) + @step_name = 'process' + @format_name = opts['format_name'] + @post_scripts_path = File.expand_path('../../post_process', __FILE__) + @processed_done = "#{@recording_dir}/status/processed/#{@meeting_id}-#{@format_name}.done" + @processed_fail = "#{@recording_dir}/status/processed/#{@meeting_id}-#{@format_name}.fail" + end + + end + end +end diff --git a/record-and-playback/core/scripts/workers/rap-publish-worker.rb b/record-and-playback/core/scripts/workers/rap-publish-worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..ff8df6bb6905f062dde1c9e033502417be00ef63 --- /dev/null +++ b/record-and-playback/core/scripts/workers/rap-publish-worker.rb @@ -0,0 +1,129 @@ +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../workers', __FILE__) +require 'custom_hash' + +module BigBlueButton + module Resque + class PublishWorker < BaseWorker + @queue = 'rap:publish' + + def perform + super do + @logger.info("Running publish worker for #{@full_id}:#{@format_name}") + + script = File.expand_path("../../publish/#{@format_name}.rb", __FILE__) + if File.exist?(script) + @publisher.put_publish_started(@format_name, @meeting_id) + + # If the publish directory exists, the script does nothing + FileUtils.rm_rf("#{@recording_dir}/publish/#{@format_name}/#{@full_id}") + remove_status_files + + # For legacy reasons, the meeting ID passed to the publish script contains + # the playback format name. + if @break_timestamp.nil? + ret, step_time = run_script(script, '-m', "#{@meeting_id}-#{@format_name}") + else + ret, step_time = run_script(script, '-m', "#{@meeting_id}-#{@format_name}", '-b', @break_timestamp) + end + step_succeeded = ( + ret.zero? && + File.exist?(@published_done) && !File.exist?(@published_fail) + ) + + props = BigBlueButton.read_props + published_dir = props['published_dir'] + + playback = {} + metadata = {} + download = {} + raw_size = {} + start_time = {} + end_time = {} + metadata_xml_path = "#{published_dir}/#{@format_name}/#{@full_id}/metadata.xml" + if File.exist?(metadata_xml_path) + begin + doc = Hash.from_xml(File.open(metadata_xml_path)) + playback = doc[:recording][:playback] unless doc[:recording][:playback].nil? + metadata = doc[:recording][:meta] unless doc[:recording][:meta].nil? + download = doc[:recording][:download] unless doc[:recording][:download].nil? + raw_size = doc[:recording][:raw_size] unless doc[:recording][:raw_size].nil? + start_time = doc[:recording][:start_time] unless doc[:recording][:start_time].nil? + end_time = doc[:recording][:end_time] unless doc[:recording][:end_time].nil? + rescue Exception => e + BigBlueButton.logger.warn 'An exception occurred while loading the extra information for the publish event' + BigBlueButton.logger.warn e.message + e.backtrace.each do |traceline| + BigBlueButton.logger.warn traceline + end + end + else + BigBlueButton.logger.warn "Couldn't find the metadata file at #{metadata_xml_path}" + end + + @publisher.put_publish_ended( + @format_name, @meeting_id, { + 'success': step_succeeded, + 'step_time': step_time, + 'playback': playback, + 'metadata': metadata, + 'download': download, + 'raw_size': raw_size, + 'start_time': start_time, + 'end_time': end_time, + }) + else + @logger.warn("Processed recording found for #{@meeting_id}/#{@format_name}, but no publish script exists") + step_succeeded = true + end + + if step_succeeded + @logger.info("Publish format succeeded for #{@full_id}:#{@format_name}") + FileUtils.rm_rf("#{@recording_dir}/process/#{@format_name}/#{@full_id}") + FileUtils.rm_rf("#{@recording_dir}/publish/#{@format_name}/#{@full_id}") + + run_post_scripts(@post_scripts_path) + else + @logger.error("Publish format failed for #{@full_id}:#{@format_name}") + FileUtils.touch(@published_fail) + end + + step_succeeded + end + end + + def remove_status_files + FileUtils.rm_f(@published_done) + FileUtils.rm_f(@published_fail) + end + + def initialize(opts) + super(opts) + @step_name = 'publish' + @format_name = opts['format_name'] + @post_scripts_path = File.expand_path('../../post_publish', __FILE__) + @published_done = "#{@recording_dir}/status/published/#{@meeting_id}-#{@format_name}.done" + @published_fail = "#{@recording_dir}/status/published/#{@meeting_id}-#{@format_name}.fail" + end + + end + end +end diff --git a/record-and-playback/core/scripts/workers/rap-sanity-worker.rb b/record-and-playback/core/scripts/workers/rap-sanity-worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..7b289623d9c8ea22a5c9ade861a73b574a3a46bb --- /dev/null +++ b/record-and-playback/core/scripts/workers/rap-sanity-worker.rb @@ -0,0 +1,74 @@ +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../workers', __FILE__) + +module BigBlueButton + module Resque + class SanityWorker < BaseWorker + @queue = 'rap:sanity' + + def perform + super do + @logger.info("Running sanity worker for #{@full_id}") + @publisher.put_sanity_started(@meeting_id) + + remove_status_files + + script = File.expand_path('../../sanity/sanity.rb', __FILE__) + if @break_timestamp.nil? + ret, step_time = run_script(script, '-m', @meeting_id) + else + ret, step_time = run_script(script, '-m', @meeting_id, '-b', @break_timestamp) + end + step_succeeded = (ret.zero? && File.exist?(@sanity_done)) + + @publisher.put_sanity_ended( + @meeting_id, { + success: step_succeeded, + step_time: step_time, + }) + + if step_succeeded + @logger.info("Successfully sanity checked #{@full_id}") + run_post_scripts(@post_scripts_path) + else + @logger.error("Sanity check failed on #{@full_id}") + FileUtils.touch(@sanity_fail) + end + + step_succeeded + end + end + + def remove_status_files + FileUtils.rm_f(@sanity_done) + FileUtils.rm_f(@sanity_fail) + end + + def initialize(opts) + super(opts) + @step_name = 'sanity' + @post_scripts_path = File.expand_path('../../post_archive', __FILE__) + @sanity_fail = "#{@recording_dir}/status/sanity/#{@meeting_id}.fail" + @sanity_done = "#{@recording_dir}/status/sanity/#{@meeting_id}.done" + end + end + end +end diff --git a/record-and-playback/core/scripts/workers/workers.rb b/record-and-playback/core/scripts/workers/workers.rb new file mode 100644 index 0000000000000000000000000000000000000000..6d689ff546cdbd02768c9c0d09d983e70c15030d --- /dev/null +++ b/record-and-playback/core/scripts/workers/workers.rb @@ -0,0 +1,25 @@ +# encoding: utf-8 + +# Copyright â“’ 2017 BigBlueButton Inc. and by respective authors. +# +# This file is part of BigBlueButton open source conferencing system. +# +# BigBlueButton 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 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 File.expand_path('../rap-base-worker.rb', __FILE__) +require File.expand_path('../rap-archive-worker.rb', __FILE__) +require File.expand_path('../rap-sanity-worker.rb', __FILE__) +require File.expand_path('../rap-process-worker.rb', __FILE__) +require File.expand_path('../rap-publish-worker.rb', __FILE__) +require File.expand_path('../rap-captions-worker.rb', __FILE__) diff --git a/record-and-playback/core/systemd/bbb-rap-archive-worker.service b/record-and-playback/core/systemd/bbb-rap-archive-worker.service deleted file mode 100644 index 650f40060edcea546969be845b00e1ebef55f902..0000000000000000000000000000000000000000 --- a/record-and-playback/core/systemd/bbb-rap-archive-worker.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=BigBlueButton recording and playback archive worker -ConditionPathExistsGlob=/var/bigbluebutton/recording/status/recorded/*.done - -[Service] -Type=simple -ExecStart=/usr/local/bigbluebutton/core/scripts/rap-archive-worker.rb -WorkingDirectory=/usr/local/bigbluebutton/core/scripts -User=bigbluebutton -Slice=bbb_record_core.slice diff --git a/record-and-playback/core/systemd/bbb-rap-process-worker.service b/record-and-playback/core/systemd/bbb-rap-process-worker.service deleted file mode 100644 index bc876c34b159786f0d70a4daa5ac4740003df212..0000000000000000000000000000000000000000 --- a/record-and-playback/core/systemd/bbb-rap-process-worker.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=BigBlueButton recording and playback process worker -ConditionPathExistsGlob=/var/bigbluebutton/recording/status/sanity/*.done - -[Service] -Type=simple -ExecStart=/usr/local/bigbluebutton/core/scripts/rap-process-worker.rb -WorkingDirectory=/usr/local/bigbluebutton/core/scripts -User=bigbluebutton -Slice=bbb_record_core.slice diff --git a/record-and-playback/core/systemd/bbb-rap-publish-worker.service b/record-and-playback/core/systemd/bbb-rap-publish-worker.service deleted file mode 100644 index ef84f18e02c4e7221e074b0d70a2f49640f846cc..0000000000000000000000000000000000000000 --- a/record-and-playback/core/systemd/bbb-rap-publish-worker.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=BigBlueButton recording and playback publish worker -ConditionPathExistsGlob=/var/bigbluebutton/recording/status/processed/*.done - -[Service] -Type=simple -ExecStart=/usr/local/bigbluebutton/core/scripts/rap-publish-worker.rb -WorkingDirectory=/usr/local/bigbluebutton/core/scripts -User=bigbluebutton -Slice=bbb_record_core.slice diff --git a/record-and-playback/core/systemd/bbb-rap-resque-worker.service b/record-and-playback/core/systemd/bbb-rap-resque-worker.service new file mode 100644 index 0000000000000000000000000000000000000000..71467bfc8ff03aa0cf34b5882467f7aa257042bd --- /dev/null +++ b/record-and-playback/core/systemd/bbb-rap-resque-worker.service @@ -0,0 +1,16 @@ +[Unit] +Description=BigBlueButton resque worker for recordings + +[Service] +Type=simple +ExecStart=/bin/sh -c '/usr/bin/rake -f ../Rakefile resque:workers >> /var/log/bigbluebutton/bbb-rap-worker.log' +WorkingDirectory=/usr/local/bigbluebutton/core/scripts +Environment=QUEUE=rap:archive,rap:publish,rap:process,rap:sanity,rap:captions +Environment=COUNT=1 +# Environment=VVERBOSE=1 +User=bigbluebutton +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target diff --git a/record-and-playback/core/systemd/bbb-rap-sanity-worker.service b/record-and-playback/core/systemd/bbb-rap-sanity-worker.service deleted file mode 100644 index 226f54e67fcec02734ad66f966a83fc7834530f8..0000000000000000000000000000000000000000 --- a/record-and-playback/core/systemd/bbb-rap-sanity-worker.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=BigBlueButton recording and playback sanity check worker -ConditionPathExistsGlob=/var/bigbluebutton/recording/status/archived/*.done - -[Service] -Type=simple -ExecStart=/usr/local/bigbluebutton/core/scripts/rap-sanity-worker.rb -WorkingDirectory=/usr/local/bigbluebutton/core/scripts -User=bigbluebutton -Slice=bbb_record_core.slice diff --git a/record-and-playback/core/systemd/bbb-rap-starter.service b/record-and-playback/core/systemd/bbb-rap-starter.service new file mode 100644 index 0000000000000000000000000000000000000000..d9cc670d32e0c087251aaeb8de5ccee84260e29c --- /dev/null +++ b/record-and-playback/core/systemd/bbb-rap-starter.service @@ -0,0 +1,10 @@ +[Unit] +Description=BigBlueButton recording processing starter + +[Service] +Type=simple +ExecStart=/usr/local/bigbluebutton/core/scripts/rap-starter.rb +WorkingDirectory=/usr/local/bigbluebutton/core/scripts +User=bigbluebutton +Slice=bbb_record_core.slice +Restart=on-failure diff --git a/record-and-playback/core/systemd/bbb-record-core.target b/record-and-playback/core/systemd/bbb-record-core.target index 0c4eb7120d435c272afb0fdde8dd3ad7fc1ff9c6..c5070bab8faa344b43d83ec5c211356ab433277a 100755 --- a/record-and-playback/core/systemd/bbb-record-core.target +++ b/record-and-playback/core/systemd/bbb-record-core.target @@ -1,4 +1,4 @@ [Unit] Description=BigBlueButton recording & playback processing -Wants=bbb-rap-archive-worker.service bbb-rap-sanity-worker.service bbb-rap-process-worker.service bbb-rap-publish-worker.service bbb-rap-events-worker.service +Wants=bbb-rap-events-worker.service StopWhenUnneeded=true diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb index d7d594eb839720174d67b132c752fec34a297419..be381036acf276c9f68b974e7ca982d45fe83c0f 100755 --- a/record-and-playback/presentation/scripts/process/presentation.rb +++ b/record-and-playback/presentation/scripts/process/presentation.rb @@ -38,7 +38,7 @@ end meeting_id = opts[:meeting_id] # This script lives in scripts/archive/steps while properties.yaml lives in scripts/ -props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml')) +props = BigBlueButton.read_props presentation_props = YAML::load(File.open('presentation.yml')) presentation_props['audio_offset'] = 0 if presentation_props['audio_offset'].nil? presentation_props['include_deskshare'] = false if presentation_props['include_deskshare'].nil? diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index ed4ca97b5d9b02bc5b09588648ec55eba34b5240..56caa5238a985292786f4d78bf1cd3389c26e9a7 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -21,7 +21,7 @@ performance_start = Time.now -require '../../core/lib/recordandplayback' +require File.expand_path('../../../lib/recordandplayback', __FILE__) require 'rubygems' require 'trollop' require 'yaml' @@ -30,7 +30,7 @@ require 'fastimage' # require fastimage to get the image size of the slides (gem # This script lives in scripts/archive/steps while properties.yaml lives in scripts/ -bbb_props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml')) +bbb_props = BigBlueButton.read_props $presentation_props = YAML::load(File.open('presentation.yml')) # There's a couple of places where stuff is mysteriously divided or multiplied