diff --git a/docs/docs/administration/configuration-files.md b/docs/docs/administration/configuration-files.md index d58741d8e89a..97186e593f6b 100644 --- a/docs/docs/administration/configuration-files.md +++ b/docs/docs/administration/configuration-files.md @@ -32,8 +32,9 @@ Starting with BigBlueButton 2.3 many of the configuration files have local overr | /usr/local/bigbluebutton/bbb-webrtc-sfu/config/default.yml | /etc/bigbluebutton/bbb-webrtc-sfu/production.yml | Arrays are merged by replacement | | /usr/local/bigbluebutton/bbb-pads/config/settings.json | /etc/bigbluebutton/bbb-pads.json | Arrays are merged by replacement | | /usr/share/bbb-shared-notes-server/config/default.yml | /etc/bigbluebutton/bbb-shared-notes-server.yml | | -| /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml | /etc/bigbluebutton/recording/recording.yml | +| /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml | /etc/bigbluebutton/recording/recording.yml | Honored by all recording formats; re-read on every processing step, so changes do not require a restart | | /usr/local/bigbluebutton/core/scripts/presentation.yml | /etc/bigbluebutton/recording/presentation.yml | +| /usr/local/bigbluebutton/core/scripts/video.yml | /etc/bigbluebutton/recording/video.yml | Individual `presets` are merged by key; other settings are replaced | | /etc/cron.daily/bigbluebutton | /etc/default/bigbluebutton-cron-config | Only variables allowed in the override

@@ -66,6 +67,15 @@ services { } ``` +For the recording pipeline, `/etc/bigbluebutton/recording/recording.yml` overrides settings from `/usr/local/bigbluebutton/core/scripts/bigbluebutton.yml` for every recording format (presentation, video, screenshare, notes, podcast). For example, the following `recording.yml` makes published recording links use a different hostname and protocol: + +```yml +playback_host: playback.example.com +playback_protocol: https +``` + +The override is re-read on every recording processing step, so a change takes effect for the next recording without restarting the recording workers. The playback link in a recording's `metadata.xml` is regenerated at publish time, so links of an already-published recording can be corrected by rebuilding it: `bbb-record --rebuild `. + ## HTML5 Client ### Configuration files diff --git a/record-and-playback/core/lib/recordandplayback.rb b/record-and-playback/core/lib/recordandplayback.rb index 04410ee8cd58..24257a20b696 100644 --- a/record-and-playback/core/lib/recordandplayback.rb +++ b/record-and-playback/core/lib/recordandplayback.rb @@ -237,19 +237,16 @@ def self.rap_scripts_path File.join(BigBlueButton.rap_core_path, 'scripts') end - def self.read_props - return @props if @props - - filepathRecOverride = "/etc/bigbluebutton/recording/recording.yml" - hasOverride = File.file?(filepathRecOverride) - + # Do not cache the result: long-lived processes (e.g. the resque workers) + # must pick up changes to the override file without a restart. + def self.read_props(override_path: '/etc/bigbluebutton/recording/recording.yml') filepath = File.join(BigBlueButton.rap_scripts_path, 'bigbluebutton.yml') - @props = YAML::load(File.open(filepath)) - if (hasOverride) - recOverrideProps = YAML::load(File.open(filepathRecOverride)) - @props = @props.merge(recOverrideProps) + props = YAML.safe_load(File.read(filepath), aliases: true) || {} + if File.file?(override_path) + override_props = YAML.safe_load(File.read(override_path), aliases: true) || {} + props = props.merge(override_props) end - @props + props end def self.create_redis_publisher diff --git a/record-and-playback/core/test/recordandplayback/test_read_props.rb b/record-and-playback/core/test/recordandplayback/test_read_props.rb new file mode 100644 index 000000000000..d5174823d7bb --- /dev/null +++ b/record-and-playback/core/test/recordandplayback/test_read_props.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require 'tmpdir' + +require 'recordandplayback' + +class TestReadProps < Minitest::Test + def test_returns_base_props_when_override_missing + props = BigBlueButton.read_props(override_path: '/nonexistent/recording.yml') + + assert_kind_of(Hash, props) + refute_nil(props['playback_host']) + end + + def test_override_merges_over_base_props + Dir.mktmpdir do |dir| + override = File.join(dir, 'recording.yml') + File.write(override, "playback_host: override.example.com\n") + + props = BigBlueButton.read_props(override_path: override) + + assert_equal('override.example.com', props['playback_host']) + refute_nil(props['recording_dir']) + end + end + + def test_rereads_override_on_each_call + Dir.mktmpdir do |dir| + override = File.join(dir, 'recording.yml') + + File.write(override, "playback_host: first.example.com\n") + assert_equal('first.example.com', BigBlueButton.read_props(override_path: override)['playback_host']) + + File.write(override, "playback_host: second.example.com\n") + assert_equal('second.example.com', BigBlueButton.read_props(override_path: override)['playback_host']) + end + end + + def test_empty_override_file_is_ignored + Dir.mktmpdir do |dir| + override = File.join(dir, 'recording.yml') + File.write(override, '') + + props = BigBlueButton.read_props(override_path: override) + + assert_kind_of(Hash, props) + refute_nil(props['playback_host']) + end + end +end diff --git a/record-and-playback/notes/scripts/process/notes.rb b/record-and-playback/notes/scripts/process/notes.rb index f9ba7690c4dd..1114bcc9429f 100644 --- a/record-and-playback/notes/scripts/process/notes.rb +++ b/record-and-playback/notes/scripts/process/notes.rb @@ -38,7 +38,7 @@ 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 notes_props = YAML::load(File.open('notes.yml')) format = notes_props['format'] diff --git a/record-and-playback/notes/scripts/publish/notes.rb b/record-and-playback/notes/scripts/publish/notes.rb index 12963d9d6529..0a5176d8d78a 100644 --- a/record-and-playback/notes/scripts/publish/notes.rb +++ b/record-and-playback/notes/scripts/publish/notes.rb @@ -30,7 +30,7 @@ # 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 notes_props = YAML::load(File.open('notes.yml')) opts = Optimist::options do diff --git a/record-and-playback/podcast/scripts/process/podcast.rb b/record-and-playback/podcast/scripts/process/podcast.rb index de23d648d4ec..ed02a131e975 100755 --- a/record-and-playback/podcast/scripts/process/podcast.rb +++ b/record-and-playback/podcast/scripts/process/podcast.rb @@ -38,7 +38,7 @@ 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 podcast_props = YAML::load(File.open('podcast.yml')) recording_dir = props['recording_dir'] diff --git a/record-and-playback/podcast/scripts/publish/podcast.rb b/record-and-playback/podcast/scripts/publish/podcast.rb index bc07e401f491..fc877017a704 100755 --- a/record-and-playback/podcast/scripts/publish/podcast.rb +++ b/record-and-playback/podcast/scripts/publish/podcast.rb @@ -30,7 +30,7 @@ # 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 podcast_props = YAML::load(File.open('podcast.yml')) opts = Optimist::options do diff --git a/record-and-playback/screenshare/scripts/process/screenshare.rb b/record-and-playback/screenshare/scripts/process/screenshare.rb index 9b023a7f8247..b97f4f87ac87 100644 --- a/record-and-playback/screenshare/scripts/process/screenshare.rb +++ b/record-and-playback/screenshare/scripts/process/screenshare.rb @@ -40,7 +40,7 @@ end # Load parameters and set up paths -props = YAML::load(File.open(File.expand_path('../../bigbluebutton.yml', __FILE__))) +props = BigBlueButton.read_props screenshare_props = YAML::load(File.open(File.expand_path('../../screenshare.yml', __FILE__))) recording_dir = props['recording_dir'] diff --git a/record-and-playback/screenshare/scripts/publish/screenshare.rb b/record-and-playback/screenshare/scripts/publish/screenshare.rb index 18734effd771..7252c47102e3 100644 --- a/record-and-playback/screenshare/scripts/publish/screenshare.rb +++ b/record-and-playback/screenshare/scripts/publish/screenshare.rb @@ -37,7 +37,7 @@ end # Load parameters and set up paths -props = YAML::load(File.open(File.expand_path('../../bigbluebutton.yml', __FILE__))) +props = BigBlueButton.read_props screenshare_props = YAML::load(File.open(File.expand_path('../../screenshare.yml', __FILE__))) process_dir = "#{props['recording_dir']}/process/screenshare/#{meeting_id}" @@ -81,8 +81,16 @@ "#{publish_dir}/caption_#{caption['locale']}.vtt") end -# Copy over metadata xml file -FileUtils.cp("#{process_dir}/metadata.xml", "#{publish_dir}/metadata.xml") +# Refresh the playback link from current props (honoring recording.yml +# overrides) and write the updated metadata to the publish directory, rather +# than copying the process-time metadata.xml with a possibly-stale link. +metadata_xml = Nokogiri::XML(File.open("#{process_dir}/metadata.xml")) +link = metadata_xml.at_xpath('/recording/playback/link') +if link + link.content = "#{props['playback_protocol']}://#{props['playback_host']}/recording/screenshare/#{meeting_id}/" + logger.info "Refreshed playback link to #{link.content}" +end +File.write("#{publish_dir}/metadata.xml", metadata_xml.to_xml) # Copy over css and js support files FileUtils.cp_r("#{process_dir}/css", publish_dir) diff --git a/record-and-playback/video/scripts/publish/video.rb b/record-and-playback/video/scripts/publish/video.rb index e0fd98755f4a..0888ecc5ca7c 100755 --- a/record-and-playback/video/scripts/publish/video.rb +++ b/record-and-playback/video/scripts/publish/video.rb @@ -128,8 +128,15 @@ "#{publish_dir}/caption_#{caption['locale']}.vtt") end -# Copy over metadata xml file -FileUtils.cp("#{process_dir}/metadata.xml", "#{publish_dir}/metadata.xml") +# Refresh the playback link from current props (honoring recording.yml +# overrides) and write the updated metadata to the publish directory, rather +# than copying the process-time metadata.xml with a possibly-stale link. +link = metadata_xml.at_xpath('/recording/playback/link') +if link + link.content = "#{props['playback_protocol']}://#{props['playback_host']}/playback/video/#{meeting_id}/" + logger.info("Refreshed playback link to #{link.content}") +end +File.write("#{publish_dir}/metadata.xml", metadata_xml.to_xml) # Get raw size of presentation files raw_dir = "#{recording_dir}/raw/#{meeting_id}"