2012-05-04 02:56:01 +08:00
|
|
|
# Set encoding to utf-8
|
|
|
|
# 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).
|
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
#
|
|
|
|
|
2020-01-22 06:14:39 +08:00
|
|
|
require_relative 'boot'
|
2011-05-25 08:41:44 +08:00
|
|
|
|
2011-05-10 02:20:35 +08:00
|
|
|
require 'recordandplayback/events_archiver'
|
2011-04-27 04:38:10 +08:00
|
|
|
require 'recordandplayback/generators/events'
|
2011-04-26 02:06:32 +08:00
|
|
|
require 'recordandplayback/generators/audio'
|
2011-05-11 04:17:57 +08:00
|
|
|
require 'recordandplayback/generators/video'
|
2011-04-26 02:06:32 +08:00
|
|
|
require 'recordandplayback/generators/audio_processor'
|
2011-05-11 00:31:49 +08:00
|
|
|
require 'recordandplayback/generators/presentation'
|
2011-07-16 06:09:11 +08:00
|
|
|
require 'open4'
|
2013-06-18 01:23:45 +08:00
|
|
|
require 'pp'
|
2014-11-13 03:28:33 +08:00
|
|
|
require 'absolute_time'
|
2016-09-04 04:08:32 +08:00
|
|
|
require 'logger'
|
2015-06-05 00:03:20 +08:00
|
|
|
require 'find'
|
2015-06-09 22:39:35 +08:00
|
|
|
require 'rubygems'
|
2019-04-04 21:09:49 +08:00
|
|
|
require 'net/http'
|
2019-12-07 04:06:32 +08:00
|
|
|
require 'journald/logger'
|
2020-09-24 04:16:57 +08:00
|
|
|
require 'shellwords'
|
|
|
|
require 'English'
|
2011-05-11 00:31:49 +08:00
|
|
|
|
2011-04-26 03:29:33 +08:00
|
|
|
module BigBlueButton
|
2011-05-10 05:24:41 +08:00
|
|
|
class MissingDirectoryException < RuntimeError
|
2011-05-07 05:26:22 +08:00
|
|
|
end
|
2019-04-04 21:09:49 +08:00
|
|
|
|
2011-05-10 05:24:41 +08:00
|
|
|
class FileNotFoundException < RuntimeError
|
2011-05-07 05:26:22 +08:00
|
|
|
end
|
2013-06-15 04:39:29 +08:00
|
|
|
|
|
|
|
class ExecutionStatus
|
|
|
|
def initialize
|
|
|
|
@output = []
|
|
|
|
@errors = []
|
|
|
|
@detailedStatus = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
attr_accessor :output
|
|
|
|
attr_accessor :errors
|
|
|
|
attr_accessor :detailedStatus
|
|
|
|
|
|
|
|
def success?
|
|
|
|
@detailedStatus.success?
|
|
|
|
end
|
|
|
|
|
|
|
|
def exited?
|
|
|
|
@detailedStatus.exited?
|
|
|
|
end
|
|
|
|
|
|
|
|
def exitstatus
|
|
|
|
@detailedStatus.exitstatus
|
|
|
|
end
|
|
|
|
end
|
2019-04-04 21:09:49 +08:00
|
|
|
|
2011-04-26 03:29:33 +08:00
|
|
|
# BigBlueButton logs information about its progress.
|
|
|
|
# Replace with your own logger if you desire.
|
|
|
|
#
|
|
|
|
# @param [Logger] log your own logger
|
|
|
|
# @return [Logger] the logger you set
|
|
|
|
def self.logger=(log)
|
|
|
|
@logger = log
|
|
|
|
end
|
2019-04-04 21:09:49 +08:00
|
|
|
|
2011-04-26 03:29:33 +08:00
|
|
|
# Get BigBlueButton logger.
|
|
|
|
#
|
|
|
|
# @return [Logger]
|
|
|
|
def self.logger
|
|
|
|
return @logger if @logger
|
2019-12-07 04:06:32 +08:00
|
|
|
|
|
|
|
logger = Journald::Logger.new('bbb-rap')
|
2011-04-26 03:29:33 +08:00
|
|
|
logger.level = Logger::INFO
|
|
|
|
@logger = logger
|
|
|
|
end
|
2014-11-13 03:28:33 +08:00
|
|
|
|
|
|
|
def self.redis_publisher=(publisher)
|
|
|
|
@redis_publisher = publisher
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.redis_publisher
|
|
|
|
return @redis_publisher
|
|
|
|
end
|
2019-04-04 21:09:49 +08:00
|
|
|
|
|
|
|
def self.execute(command, fail_on_error = true)
|
2020-09-24 04:16:57 +08:00
|
|
|
BigBlueButton.logger.info("Executing: #{command.respond_to?(:to_ary) ? Shellwords.join(command) : command}")
|
|
|
|
IO.popen(command, err: %i[child out]) do |io|
|
|
|
|
io.each_line do |line|
|
|
|
|
BigBlueButton.logger.info(line.chomp)
|
2019-04-04 21:09:49 +08:00
|
|
|
end
|
2011-05-07 05:26:22 +08:00
|
|
|
end
|
2020-09-24 04:16:57 +08:00
|
|
|
status = $CHILD_STATUS
|
|
|
|
|
2013-06-15 04:39:29 +08:00
|
|
|
BigBlueButton.logger.info("Success?: #{status.success?}")
|
2011-06-30 06:39:25 +08:00
|
|
|
BigBlueButton.logger.info("Process exited? #{status.exited?}")
|
|
|
|
BigBlueButton.logger.info("Exit status: #{status.exitstatus}")
|
2020-09-24 04:16:57 +08:00
|
|
|
raise 'Execution failed' if status.success? == false && fail_on_error
|
|
|
|
|
2013-06-15 04:39:29 +08:00
|
|
|
status
|
2011-05-07 05:26:22 +08:00
|
|
|
end
|
2013-06-18 01:23:45 +08:00
|
|
|
|
2013-08-26 23:20:33 +08:00
|
|
|
def self.exec_ret(*command)
|
2020-09-24 04:16:57 +08:00
|
|
|
execute(command, false).exitstatus
|
2013-08-26 23:20:33 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.exec_redirect_ret(outio, *command)
|
2020-09-24 04:16:57 +08:00
|
|
|
BigBlueButton.logger.info "Executing: #{Shellwords.join(command)}"
|
2013-08-26 23:20:33 +08:00
|
|
|
BigBlueButton.logger.info "Sending output to #{outio}"
|
|
|
|
IO.pipe do |r, w|
|
|
|
|
pid = spawn(*command, :out => outio, :err => w)
|
|
|
|
w.close
|
2016-08-02 22:58:42 +08:00
|
|
|
r.each_line do |line|
|
2013-08-26 23:20:33 +08:00
|
|
|
BigBlueButton.logger.info line.chomp
|
|
|
|
end
|
|
|
|
Process.waitpid(pid)
|
|
|
|
BigBlueButton.logger.info "Exit status: #{$?.exitstatus}"
|
|
|
|
return $?.exitstatus
|
|
|
|
end
|
|
|
|
end
|
2013-09-25 02:03:44 +08:00
|
|
|
|
2013-06-18 01:23:45 +08:00
|
|
|
def self.hash_to_str(hash)
|
|
|
|
return PP.pp(hash, "")
|
|
|
|
end
|
2014-11-13 03:28:33 +08:00
|
|
|
|
|
|
|
def self.monotonic_clock()
|
|
|
|
return (AbsoluteTime.now * 1000).to_i
|
|
|
|
end
|
2017-07-10 23:42:40 +08:00
|
|
|
|
2019-03-06 00:17:48 +08:00
|
|
|
def self.download(url, output)
|
|
|
|
BigBlueButton.logger.info "Downloading #{url} to #{output}"
|
|
|
|
|
|
|
|
uri = URI.parse(url)
|
|
|
|
if ["http", "https", "ftp"].include? uri.scheme
|
2019-04-15 23:36:05 +08:00
|
|
|
response = Net::HTTP.start(uri.host, uri.port) {|http|
|
|
|
|
http.head(uri.request_uri)
|
2019-04-04 21:09:49 +08:00
|
|
|
}
|
2019-04-15 23:36:05 +08:00
|
|
|
unless response.is_a? Net::HTTPSuccess
|
2019-06-06 22:55:53 +08:00
|
|
|
raise "File not available: #{response.message}"
|
2019-04-04 21:09:49 +08:00
|
|
|
end
|
2019-03-06 00:17:48 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
if uri.scheme.nil?
|
|
|
|
url = "file://" + url
|
2019-04-04 21:09:49 +08:00
|
|
|
uri = URI.parse(url)
|
2019-03-06 00:17:48 +08:00
|
|
|
end
|
|
|
|
|
2019-04-04 21:09:49 +08:00
|
|
|
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
|
|
request = Net::HTTP::Get.new uri.request_uri
|
|
|
|
http.request request do |response|
|
|
|
|
open output, 'w' do |io|
|
|
|
|
response.read_body do |chunk|
|
|
|
|
io.write chunk
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-03-06 00:17:48 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.try_download(url, output)
|
|
|
|
begin
|
|
|
|
self.download(url, output)
|
|
|
|
rescue Exception => e
|
|
|
|
BigBlueButton.logger.error "Failed to download file: #{e.to_s}"
|
|
|
|
FileUtils.rm_f output
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-06-05 00:03:20 +08:00
|
|
|
def self.get_dir_size(dir_name)
|
|
|
|
size = 0
|
|
|
|
if FileTest.directory?(dir_name)
|
2019-04-04 21:09:49 +08:00
|
|
|
Find.find(dir_name) {|f| size += File.size(f)}
|
2015-06-05 00:03:20 +08:00
|
|
|
end
|
2015-06-10 05:06:57 +08:00
|
|
|
size.to_s
|
2015-06-05 00:03:20 +08:00
|
|
|
end
|
2017-03-11 02:09:23 +08:00
|
|
|
|
2015-09-09 04:18:02 +08:00
|
|
|
def self.add_tag_to_xml(xml_filename, parent_xpath, tag, content)
|
|
|
|
if File.exist? xml_filename
|
2020-06-17 18:13:07 +08:00
|
|
|
doc = Nokogiri::XML(File.read(xml_filename)) {|x| x.noblanks}
|
2017-03-11 02:09:23 +08:00
|
|
|
|
2015-09-09 04:18:02 +08:00
|
|
|
node = doc.at_xpath("#{parent_xpath}/#{tag}")
|
|
|
|
node.remove if not node.nil?
|
2017-03-11 02:09:23 +08:00
|
|
|
|
2015-09-09 04:18:02 +08:00
|
|
|
node = Nokogiri::XML::Node.new tag, doc
|
|
|
|
node.content = content
|
2017-03-11 02:09:23 +08:00
|
|
|
|
2015-09-09 04:18:02 +08:00
|
|
|
doc.at(parent_xpath) << node
|
2017-03-11 02:09:23 +08:00
|
|
|
|
2015-09-09 04:18:02 +08:00
|
|
|
xml_file = File.new(xml_filename, "w")
|
|
|
|
xml_file.write(doc.to_xml(:indent => 2))
|
|
|
|
xml_file.close
|
|
|
|
end
|
|
|
|
end
|
2015-06-09 22:39:35 +08:00
|
|
|
|
2015-06-11 01:07:11 +08:00
|
|
|
def self.add_raw_size_to_metadata(dir_name, raw_dir_name)
|
|
|
|
size = BigBlueButton.get_dir_size(raw_dir_name)
|
2015-09-09 04:18:02 +08:00
|
|
|
BigBlueButton.add_tag_to_xml("#{dir_name}/metadata.xml", "//recording", "raw_size", size)
|
2015-06-11 01:07:11 +08:00
|
|
|
end
|
|
|
|
|
2015-09-09 04:18:02 +08:00
|
|
|
def self.add_playback_size_to_metadata(dir_name)
|
2015-06-10 05:06:57 +08:00
|
|
|
size = BigBlueButton.get_dir_size(dir_name)
|
2015-09-09 04:18:02 +08:00
|
|
|
BigBlueButton.add_tag_to_xml("#{dir_name}/metadata.xml", "//recording/playback", "size", size)
|
|
|
|
end
|
2015-06-10 05:06:57 +08:00
|
|
|
|
2015-09-09 04:18:02 +08:00
|
|
|
def self.add_download_size_to_metadata(dir_name)
|
|
|
|
size = BigBlueButton.get_dir_size(dir_name)
|
|
|
|
BigBlueButton.add_tag_to_xml("#{dir_name}/metadata.xml", "//recording/download", "size", size)
|
2015-06-09 22:39:35 +08:00
|
|
|
end
|
2017-06-08 02:36:48 +08:00
|
|
|
|
|
|
|
def self.record_id_to_timestamp(r)
|
|
|
|
r.split("-")[1].to_i / 1000
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.done_to_timestamp(r)
|
|
|
|
BigBlueButton.record_id_to_timestamp(File.basename(r, ".done"))
|
|
|
|
end
|
2017-10-10 03:37:28 +08:00
|
|
|
|
2020-02-06 21:43:32 +08:00
|
|
|
def self.rap_core_path
|
|
|
|
File.expand_path('../../', __FILE__)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.rap_scripts_path
|
|
|
|
File.join(BigBlueButton.rap_core_path, 'scripts')
|
|
|
|
end
|
|
|
|
|
2017-07-10 23:42:40 +08:00
|
|
|
def self.read_props
|
|
|
|
return @props if @props
|
2020-02-06 21:43:32 +08:00
|
|
|
|
2022-04-01 03:37:46 +08:00
|
|
|
filepathRecOverride = "/etc/bigbluebutton/recording/recording.yml"
|
2022-03-21 21:47:01 +08:00
|
|
|
hasOverride = File.file?(filepathRecOverride)
|
2022-03-18 23:16:27 +08:00
|
|
|
|
2020-02-06 21:43:32 +08:00
|
|
|
filepath = File.join(BigBlueButton.rap_scripts_path, 'bigbluebutton.yml')
|
2022-03-21 21:47:01 +08:00
|
|
|
@props = YAML::load(File.open(filepath))
|
|
|
|
if (hasOverride)
|
2022-03-18 23:16:27 +08:00
|
|
|
recOverrideProps = YAML::load(File.open(filepathRecOverride))
|
2022-03-21 21:47:01 +08:00
|
|
|
@props = @props.merge(recOverrideProps)
|
2022-03-18 23:16:27 +08:00
|
|
|
end
|
2022-04-20 21:00:07 +08:00
|
|
|
@props
|
2017-07-10 23:42:40 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.create_redis_publisher
|
|
|
|
props = BigBlueButton.read_props
|
|
|
|
redis_host = props['redis_host']
|
|
|
|
redis_port = props['redis_port']
|
2019-12-06 22:50:41 +08:00
|
|
|
redis_password = props['redis_password']
|
|
|
|
BigBlueButton.redis_publisher = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password)
|
2017-07-10 23:42:40 +08:00
|
|
|
end
|
2011-05-25 08:41:44 +08:00
|
|
|
end
|