From 869f2892909033f5eae8243439844d444f439d01 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sun, 5 Dec 2021 15:37:47 +0100 Subject: [PATCH 01/26] Update README instructions to setup a recording development environment --- record-and-playback/core/scripts/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/record-and-playback/core/scripts/README b/record-and-playback/core/scripts/README index df8adf4e6c..d067b0d154 100644 --- a/record-and-playback/core/scripts/README +++ b/record-and-playback/core/scripts/README @@ -34,7 +34,7 @@ playback_host: 10.0.3.203 which recording to process. 4. Before running the scripts, we have to make sure our scripts have the PATHs setup correctly. - Edit presentation/scripts/process/presentation.rb and uncomment the DEVELOPMENT PATH while + Edit presentation/scripts/process/presentation.rb, presentation/scripts/publish/presentation.rb, presentation/scripts/presentation.yml and uncomment the DEVELOPMENT PATH while commenting the PRODUCTION PATH. We need to do this so the script will be able to find the core library. From 25a10455b3f8a3f7b483e19ad19be61d50beaec1 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sun, 5 Dec 2021 15:53:06 +0100 Subject: [PATCH 02/26] Linting changes --- .../scripts/publish/presentation.rb | 677 +++++++++--------- 1 file changed, 322 insertions(+), 355 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index a5f3fcef0d..00b7eafa80 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Set encoding to utf-8 # encoding: UTF-8 @@ -21,7 +22,13 @@ performance_start = Time.now -require File.expand_path('../../../lib/recordandplayback', __FILE__) +# For DEVELOPMENT +# Allows us to run the script manually +require File.expand_path('../../../../core/lib/recordandplayback', __FILE__) + +# For PRODUCTION +# require File.expand_path('../../../lib/recordandplayback', __FILE__) + require 'rubygems' require 'trollop' require 'yaml' @@ -31,7 +38,7 @@ require 'json' # This script lives in scripts/archive/steps while properties.yaml lives in scripts/ bbb_props = BigBlueButton.read_props -$presentation_props = YAML::load(File.read('presentation.yml')) +$presentation_props = YAML.safe_load(File.read('presentation.yml')) # There's a couple of places where stuff is mysteriously divided or multiplied # by 2. This is just here to call out how spooky that is. @@ -41,11 +48,11 @@ def scaleToDeskshareVideo(width, height) deskshare_video_height = $presentation_props['deskshare_output_height'].to_f deskshare_video_width = $presentation_props['deskshare_output_height'].to_f - scale = [deskshare_video_width/width, deskshare_video_height/height] + scale = [deskshare_video_width / width, deskshare_video_height / height] video_width = width * scale.min video_height = height * scale.min - return video_width.floor, video_height.floor + [video_width.floor, video_height.floor] end def getDeskshareVideoDimension(deskshare_stream_name) @@ -60,7 +67,7 @@ def getDeskshareVideoDimension(deskshare_stream_name) BigBlueButton.logger.error("Could not find deskshare video: #{deskshare_video_filename}") end - return video_width, video_height + [video_width, video_height] end # @@ -86,7 +93,7 @@ end # def translateTimestamp(timestamp) new_timestamp = translateTimestamp_helper(timestamp.to_f).to_f -# BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-$meeting_start.to_f}, new value=#{new_timestamp}") + # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-$meeting_start.to_f}, new value=#{new_timestamp}") new_timestamp end @@ -99,30 +106,32 @@ def translateTimestamp_helper(timestamp) if timestamp <= event[:start_timestamp] return event[:start_timestamp] - event[:offset] # if the timestamp is during the recording period, it is just translated to the new one using the offset - elsif timestamp > event[:start_timestamp] and timestamp <= event[:stop_timestamp] + elsif (timestamp > event[:start_timestamp]) && (timestamp <= event[:stop_timestamp]) return timestamp - event[:offset] end end # if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp - return timestamp - $rec_events.last()[:offset] + $rec_events.last()[:duration] + timestamp - $rec_events.last[:offset] + $rec_events.last[:duration] end def color_to_hex(color) color = color.to_i.to_s(16) - return '0'*(6-color.length) + color + '0' * (6 - color.length) + color end def shape_scale_width(slide, x) - return (x / 100.0 * slide[:width]).round(5) + (x / 100.0 * slide[:width]).round(5) end + def shape_scale_height(slide, y) - return (y / 100.0 * slide[:height]).round(5) + (y / 100.0 * slide[:height]).round(5) end + def shape_thickness(slide, shape) if !shape[:thickness_percent].nil? - return shape_scale_width(slide, shape[:thickness_percent]) + shape_scale_width(slide, shape[:thickness_percent]) else - return shape[:thickness] + shape[:thickness] end end @@ -140,9 +149,9 @@ def svg_render_shape_pencil(g, slide, shape) BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing single point") g['style'] = "stroke:none;fill:##{shape[:color]};visibility:hidden" circle = doc.create_element('circle', - cx: shape_scale_width(slide, shape[:data_points][0]), - cy: shape_scale_height(slide, shape[:data_points][1]), - r: (shape_thickness(slide, shape) / 2.0).round(5)) + cx: shape_scale_width(slide, shape[:data_points][0]), + cy: shape_scale_height(slide, shape[:data_points][1]), + r: (shape_thickness(slide, shape) / 2.0).round(5)) g << circle else path = [] @@ -184,7 +193,7 @@ def svg_render_shape_pencil(g, slide, shape) y = shape_scale_height(slide, data_points.next) path << "M#{x} #{y}" begin - while true + loop do x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) path << "L#{x} #{y}" @@ -194,7 +203,7 @@ def svg_render_shape_pencil(g, slide, shape) end path = path.join('') - g['style'] = "stroke:##{shape[:color]};stroke-linecap:round;stroke-linejoin:round;stroke-width:#{shape_thickness(slide,shape)};visibility:hidden;fill:none" + g['style'] = "stroke:##{shape[:color]};stroke-linecap:round;stroke-linejoin:round;stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none" svg_path = doc.create_element('path', d: path) g << svg_path end @@ -202,31 +211,31 @@ end def svg_render_shape_line(g, slide, shape) g['shape'] = "line#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide,shape)};visibility:hidden;fill:none" - if $version_atleast_2_0_0 - g['style'] += ";stroke-linecap:butt" - else - g['style'] += ";stroke-linecap:round" - end + g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none" + g['style'] += if $version_atleast_2_0_0 + ';stroke-linecap:butt' + else + ';stroke-linecap:round' + end doc = g.document data_points = shape[:data_points] line = doc.create_element('line', - x1: shape_scale_width(slide, data_points[0]), - y1: shape_scale_height(slide, data_points[1]), - x2: shape_scale_width(slide, data_points[2]), - y2: shape_scale_height(slide, data_points[3])) + x1: shape_scale_width(slide, data_points[0]), + y1: shape_scale_height(slide, data_points[1]), + x2: shape_scale_width(slide, data_points[2]), + y2: shape_scale_height(slide, data_points[3])) g << line end def svg_render_shape_rect(g, slide, shape) g['shape'] = "rect#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide,shape)};visibility:hidden;fill:#{shape[:fill] ? '#'+shape[:color] : 'none'}" - if $version_atleast_2_0_0 - g['style'] += ";stroke-linejoin:miter" - else - g['style'] += ";stroke-linejoin:round" - end + g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" + g['style'] += if $version_atleast_2_0_0 + ';stroke-linejoin:miter' + else + ';stroke-linejoin:round' + end doc = g.document data_points = shape[:data_points] @@ -250,18 +259,18 @@ def svg_render_shape_rect(g, slide, shape) end path = doc.create_element('path', - d: "M#{x1} #{y1}L#{x2} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") + d: "M#{x1} #{y1}L#{x2} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") g << path end def svg_render_shape_triangle(g, slide, shape) g['shape'] = "triangle#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide,shape)};visibility:hidden;fill:#{shape[:fill] ? '#'+shape[:color] : 'none'}" - if $version_atleast_2_0_0 - g['style'] += ";stroke-linejoin:miter;stroke-miterlimit:8" - else - g['style'] += ";stroke-linejoin:round" - end + g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" + g['style'] += if $version_atleast_2_0_0 + ';stroke-linejoin:miter;stroke-miterlimit:8' + else + ';stroke-linejoin:round' + end doc = g.document data_points = shape[:data_points] @@ -273,13 +282,13 @@ def svg_render_shape_triangle(g, slide, shape) px = ((x1 + x2) / 2.0).round(5) path = doc.create_element('path', - d: "M#{px} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") + d: "M#{px} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") g << path end def svg_render_shape_ellipse(g, slide, shape) g['shape'] = "ellipse#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide,shape)};visibility:hidden;fill:#{shape[:fill] ? '#'+shape[:color] : 'none'}" + g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" doc = g.document data_points = shape[:data_points] @@ -318,7 +327,7 @@ def svg_render_shape_ellipse(g, slide, shape) path += "A#{width_r} #{height_r} 0 0 1 #{x2} #{hy}" path += "A#{width_r} #{height_r} 0 0 1 #{hx} #{y2}" path += "A#{width_r} #{height_r} 0 0 1 #{x1} #{hy}" - path += "Z" + path += 'Z' svg_path = doc.create_element('path', d: path) g << svg_path @@ -340,13 +349,11 @@ def svg_render_shape_text(g, slide, shape) switch = doc.create_element('switch') fo = doc.create_element('foreignObject', - width: width, height: height, x: x, y: y) + width: width, height: height, x: x, y: y) p = doc.create_element('p', - xmlns: 'http://www.w3.org/1999/xhtml', style: 'margin:0;padding:0') + xmlns: 'http://www.w3.org/1999/xhtml', style: 'margin:0;padding:0') shape[:text].each_line.with_index do |line, index| - if index > 0 - p << doc.create_element('br') - end + p << doc.create_element('br') if index > 0 p << doc.create_text_node(line.chomp) end fo << p @@ -376,13 +383,13 @@ def svg_render_shape_poll(g, slide, shape) # Save the poll json to a temp file IO.write(json_file, result) # Render the poll svg - ret = BigBlueButton.exec_ret('utils/gen_poll_svg', '-i', json_file, '-w', "#{width.round}", '-h', "#{height.round}", '-n', "#{num_responders}", '-o', svg_file) - raise "Failed to generate poll svg" if ret != 0 + ret = BigBlueButton.exec_ret('utils/gen_poll_svg', '-i', json_file, '-w', width.round.to_s, '-h', height.round.to_s, '-n', num_responders.to_s, '-o', svg_file) + raise 'Failed to generate poll svg' if ret != 0 # Poll image g << doc.create_element('image', - 'xlink:href' => "presentation/#{presentation}/poll_result#{poll_id}.svg", - width: width, height: height, x: x, y: y) + 'xlink:href' => "presentation/#{presentation}/poll_result#{poll_id}.svg", + width: width, height: height, x: x, y: y) end def svg_render_shape(canvas, slide, shape, image_id) @@ -391,8 +398,8 @@ def svg_render_shape(canvas, slide, shape, image_id) return end - if shape[:in] >= slide[:out] or - (!shape[:out].nil? and shape[:out] <= slide[:in]) + if (shape[:in] >= slide[:out]) || + (!shape[:out].nil? && (shape[:out] <= slide[:in])) BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is not visible during image time span") return end @@ -401,8 +408,8 @@ def svg_render_shape(canvas, slide, shape, image_id) doc = canvas.document g = doc.create_element('g', - id: "image#{image_id}-draw#{shape[:shape_id]}", class: 'shape', - timestamp: shape[:in], undo: (shape[:undo].nil? ? -1 : shape[:undo])) + id: "image#{image_id}-draw#{shape[:shape_id]}", class: 'shape', + timestamp: shape[:in], undo: (shape[:undo].nil? ? -1 : shape[:undo])) case shape[:type] when 'pencil' @@ -425,9 +432,7 @@ def svg_render_shape(canvas, slide, shape, image_id) g[:shape] = "image#{image_id}-#{g[:shape]}" - if g.element_children.length > 0 - canvas << g - end + canvas << g unless g.element_children.empty? end $svg_image_id = 1 @@ -442,35 +447,32 @@ def svg_render_image(svg, slide, shapes) BigBlueButton.logger.info("Image #{image_id}: Presentation #{slide[:presentation]} Slide #{slide[:slide]} Deskshare #{slide[:deskshare]} from #{slide[:in]} to #{slide[:out]}") - doc = svg.document image = doc.create_element('image', - id: "image#{image_id}", class: 'slide', - in: slide[:in], out: slide[:out], - 'xlink:href' => slide[:src], - width: slide[:width], height: slide[:height], x: 0, y: 0, - style: 'visibility:hidden') - image['text'] = slide[:text] if !slide[:text].nil? + id: "image#{image_id}", class: 'slide', + in: slide[:in], out: slide[:out], + 'xlink:href' => slide[:src], + width: slide[:width], height: slide[:height], x: 0, y: 0, + style: 'visibility:hidden') + image['text'] = slide[:text] unless slide[:text].nil? svg << image - if slide[:deskshare] or - shapes[slide[:presentation]].nil? or - shapes[slide[:presentation]][slide[:slide]].nil? + if slide[:deskshare] || + shapes[slide[:presentation]].nil? || + shapes[slide[:presentation]][slide[:slide]].nil? return end shapes = shapes[slide[:presentation]][slide[:slide]] canvas = doc.create_element('g', - class: 'canvas', id: "canvas#{image_id}", - image: "image#{image_id}", display: 'none') + class: 'canvas', id: "canvas#{image_id}", + image: "image#{image_id}", display: 'none') shapes.each do |shape| svg_render_shape(canvas, slide, shape, image_id) end - if canvas.element_children.length > 0 - svg << canvas - end + svg << canvas unless canvas.element_children.empty? end def panzoom_viewbox(panzoom) @@ -486,12 +488,12 @@ def panzoom_viewbox(panzoom) w = shape_scale_width(panzoom, panzoom[:width_ratio]) h = shape_scale_height(panzoom, panzoom[:height_ratio]) - return [x, y, w, h] + [x, y, w, h] end def panzooms_emit_event(rec, panzoom) if panzoom[:in] == panzoom[:out] - BigBlueButton.logger.info("Panzoom: not emitting, duration rounds to 0") + BigBlueButton.logger.info('Panzoom: not emitting, duration rounds to 0') return end @@ -511,7 +513,7 @@ end def cursors_emit_event(rec, cursor) if cursor[:in] == cursor[:out] - BigBlueButton.logger.info("Cursor: not emitting, duration rounds to 0") + BigBlueButton.logger.info('Cursor: not emitting, duration rounds to 0') return end @@ -527,7 +529,7 @@ def cursors_emit_event(rec, cursor) (panzoom[:width_ratio] / 100.0)).round(5) y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * $magic_mystery_number / 100.0)) / (panzoom[:height_ratio] / 100.0)).round(5) - if x < 0 or x > 1 or y < 0 or y > 1 + if (x < 0) || (x > 1) || (y < 0) || (y > 1) x = -1.0 y = -1.0 end @@ -557,16 +559,16 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times # for old BBB where this info isn't in the shape messages presentation = event.at_xpath('presentation') slide = event.at_xpath('pageNumber') - if presentation.nil? - presentation = current_presentation - else - presentation = presentation.text - end + presentation = if presentation.nil? + current_presentation + else + presentation.text + end if slide.nil? slide = current_slide else slide = slide.text.to_i - slide -=1 unless $version_atleast_0_9_0 + slide -= 1 unless $version_atleast_0_9_0 end # Set up the shapes data structures if needed @@ -581,21 +583,21 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times # Common properties shape[:in] = timestamp shape[:type] = event.at_xpath('type').text - shape[:data_points] = event.at_xpath('dataPoints').text.split(',').map { |p| p.to_f } + shape[:data_points] = event.at_xpath('dataPoints').text.split(',').map(&:to_f) # These can be missing in old BBB versions, there are fallbacks user_id = event.at_xpath('userId') - shape[:user_id] = user_id.text if !user_id.nil? + shape[:user_id] = user_id.text unless user_id.nil? shape_id = event.at_xpath('id') - shape[:id] = shape_id.text if !shape_id.nil? + shape[:id] = shape_id.text unless shape_id.nil? status = event.at_xpath('status') - shape[:status] = status.text if !status.nil? + shape[:status] = status.text unless status.nil? shape[:shape_id] = $svg_shape_id $svg_shape_id += 1 # Some shape-specific properties - if shape[:type] == 'pencil' or shape[:type] == 'rectangle' or - shape[:type] == 'ellipse' or shape[:type] == 'triangle' or - shape[:type] == 'line' + if (shape[:type] == 'pencil') || (shape[:type] == 'rectangle') || + (shape[:type] == 'ellipse') || (shape[:type] == 'triangle') || + (shape[:type] == 'line') shape[:color] = color_to_hex(event.at_xpath('color').text) thickness = event.at_xpath('thickness') unless thickness @@ -608,28 +610,24 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:thickness] = thickness.text.to_i end end - if shape[:type] == 'rectangle' or - shape[:type] == 'ellipse' or shape[:type] == 'triangle' - fill = event.at_xpath('fill').text - shape[:fill] = fill =~ /true/ ? true : false + if (shape[:type] == 'rectangle') || + (shape[:type] == 'ellipse') || (shape[:type] == 'triangle') + # TODO: uncomment this + # fill = event.at_xpath('fill').text + # shape[:fill] = fill =~ /true/ ? true : false + shape[:fill] = false end if shape[:type] == 'rectangle' square = event.at_xpath('square') - if !square.nil? - shape[:square] = (square.text == 'true') - end + shape[:square] = (square.text == 'true') unless square.nil? end if shape[:type] == 'ellipse' circle = event.at_xpath('circle') - if !circle.nil? - shape[:circle] = (circle.text == 'true') - end + shape[:circle] = (circle.text == 'true') unless circle.nil? end if shape[:type] == 'pencil' commands = event.at_xpath('commands') - if !commands.nil? - shape[:commands] = commands.text.split(',').map { |c| c.to_i } - end + shape[:commands] = commands.text.split(',').map(&:to_i) unless commands.nil? end if shape[:type] == 'poll_result' shape[:num_responders] = event.at_xpath('num_responders').text.to_i @@ -650,25 +648,25 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:font_color] = color_to_hex(event.at_xpath('fontColor').text) text = event.at_xpath('text') - if !text.nil? - shape[:text] = text.text - else - shape[:text] = '' - end + shape[:text] = if !text.nil? + text.text + else + '' + end end # Find the previous shape, for updates prev_shape = nil if !shape[:id].nil? # If we have a shape ID, look up the previous shape by ID - prev_shape = shapes.find_all {|s| s[:id] == shape[:id] }.last + prev_shape = shapes.find_all { |s| s[:id] == shape[:id] }.last else # No shape ID, so do heuristic matching. If the previous shape had the # same type and same first two data points, update it. last_shape = shapes.last - if last_shape[:type] == shape[:type] and - last_shape[:data_points][0] == shape[:data_points][0] and - last_shape[:data_points][1] == shape[:data_points][1] + if (last_shape[:type] == shape[:type]) && + (last_shape[:data_points][0] == shape[:data_points][0]) && + (last_shape[:data_points][1] == shape[:data_points][1]) prev_shape = last_shape end end @@ -676,7 +674,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times prev_shape[:out] = timestamp shape[:shape_unique_id] = prev_shape[:shape_unique_id] - if shape[:type] == 'pencil' and shape[:status] == 'DRAW_UPDATE' + if (shape[:type] == 'pencil') && (shape[:status] == 'DRAW_UPDATE') # BigBlueButton 2.0 quirk - 'DRAW_UPDATE' events on pencil tool only # include newly added points, rather than the full list. shape[:data_points] = prev_shape[:data_points] + shape[:data_points] @@ -695,22 +693,20 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # for old BBB where this info isn't in the undo messages presentation = event.at_xpath('presentation') slide = event.at_xpath('pageNumber') - if presentation.nil? - presentation = current_presentation - else - presentation = presentation.text - end + presentation = if presentation.nil? + current_presentation + else + presentation.text + end if slide.nil? slide = current_slide else slide = slide.text.to_i - slide -=1 unless $version_atleast_0_9_0 + slide -= 1 unless $version_atleast_0_9_0 end # Newer undo messages have the shape id, making this a lot easier shape_id = event.at_xpath('shapeId') - if !shape_id.nil? - shape_id = shape_id.text - end + shape_id = shape_id.text unless shape_id.nil? # Set up the shapes data structures if needed shapes[presentation] = {} if shapes[presentation].nil? @@ -724,11 +720,8 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # all the shapes with that id. BigBlueButton.logger.info("Undo: removing shape with ID #{shape_id} at #{timestamp}") shapes.each do |shape| - if shape[:id] == shape_id - if shape[:undo].nil? or shape[:undo] > timestamp - shape[:undo] = timestamp - end - end + next unless shape[:id] == shape_id + shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) end else # The undo command removes the most recently added shape that has not @@ -740,14 +733,11 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # of the same shape. Use that to determine which shapes to apply undo # times to. shapes.each do |shape| - if shape[:shape_unique_id] == undo_shape[:shape_unique_id] - if shape[:undo].nil? or shape[:undo] > timestamp - shape[:undo] = timestamp - end - end + next unless shape[:shape_unique_id] == undo_shape[:shape_unique_id] + shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) end else - BigBlueButton.logger.info("Undo: no applicable shapes found") + BigBlueButton.logger.info('Undo: no applicable shapes found') end end end @@ -757,30 +747,28 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times # for old BBB where this info isn't in the clear messages presentation = event.at_xpath('presentation') slide = event.at_xpath('pageNumber') - if presentation.nil? - presentation = current_presentation - else - presentation = presentation.text - end + presentation = if presentation.nil? + current_presentation + else + presentation.text + end if slide.nil? slide = current_slide else slide = slide.text.to_i - slide -=1 unless $version_atleast_0_9_0 + slide -= 1 unless $version_atleast_0_9_0 end # BigBlueButton 2.0 per-user clear features full_clear = event.at_xpath('fullClear') - if !full_clear.nil? - full_clear = (full_clear.text == 'true') - else - # Default to full clear on older versions - full_clear = true - end + full_clear = if !full_clear.nil? + (full_clear.text == 'true') + else + # Default to full clear on older versions + true + end user_id = event.at_xpath('userId') - if !user_id.nil? - user_id = user_id.text - end + user_id = user_id.text unless user_id.nil? # Set up the shapes data structures if needed shapes[presentation] = {} if shapes[presentation].nil? @@ -790,16 +778,14 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times shapes = shapes[presentation][slide] if full_clear - BigBlueButton.logger.info("Clear: removing all shapes") + BigBlueButton.logger.info('Clear: removing all shapes') else BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") end shapes.each do |shape| - if full_clear or user_id == shape[:user_id] - if shape[:undo].nil? or shape[:undo] > timestamp - shape[:undo] = timestamp - end + if full_clear || (user_id == shape[:user_id]) + shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) end end end @@ -835,24 +821,24 @@ end # Create the shapes.svg, cursors.xml, and panzooms.xml files used for # rendering the presentation area def processPresentation(package_dir) - shapes_doc = Nokogiri::XML::Document.new() + shapes_doc = Nokogiri::XML::Document.new shapes_doc.create_internal_subset('svg', '-//W3C//DTD SVG 1.1//EN', - 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd') + 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd') svg = shapes_doc.root = shapes_doc.create_element('svg', - id: 'svgfile', - style: 'position:absolute;height:600px;width:800px', - xmlns: 'http://www.w3.org/2000/svg', - 'xmlns:xlink' => 'http://www.w3.org/1999/xlink', - version: '1.1', - viewBox: '0 0 800 600') + id: 'svgfile', + style: 'position:absolute;height:600px;width:800px', + xmlns: 'http://www.w3.org/2000/svg', + 'xmlns:xlink' => 'http://www.w3.org/1999/xlink', + version: '1.1', + viewBox: '0 0 800 600') - panzooms_doc = Nokogiri::XML::Document.new() + panzooms_doc = Nokogiri::XML::Document.new panzooms_rec = panzooms_doc.root = panzooms_doc.create_element('recording', - id: 'panzoom_events') + id: 'panzoom_events') - cursors_doc = Nokogiri::XML::Document.new() + cursors_doc = Nokogiri::XML::Document.new cursors_rec = cursors_doc.root = cursors_doc.create_element('recording', - id: 'cursor_events') + id: 'cursor_events') # Current presentation/slide state current_presentation_slide = {} @@ -883,7 +869,7 @@ def processPresentation(package_dir) events_xml.xpath('/recording/event').each do |event| eventname = event['eventname'] last_timestamp = timestamp = - (translateTimestamp(event['timestamp']) / 1000.0).round(1) + (translateTimestamp(event['timestamp']) / 1000.0).round(1) # Make sure to add initial entries to the slide & panzoom lists slide_changed = slides.empty? @@ -910,21 +896,21 @@ def processPresentation(package_dir) current_height_ratio = event.at_xpath('heightRatio').text.to_f panzoom_changed = true - elsif $presentation_props['include_deskshare'] and (eventname == 'DeskshareStartedEvent' or eventname == 'StartWebRTCDesktopShareEvent') + elsif $presentation_props['include_deskshare'] && ((eventname == 'DeskshareStartedEvent') || (eventname == 'StartWebRTCDesktopShareEvent')) deskshare = true slide_changed = true - elsif $presentation_props['include_deskshare'] and (eventname == 'DeskshareStoppedEvent' or eventname == 'StopWebRTCDesktopShareEvent') + elsif $presentation_props['include_deskshare'] && ((eventname == 'DeskshareStoppedEvent') || (eventname == 'StopWebRTCDesktopShareEvent')) deskshare = false slide_changed = true - elsif eventname == 'AddShapeEvent' or eventname == 'ModifyTextEvent' + elsif (eventname == 'AddShapeEvent') || (eventname == 'ModifyTextEvent') events_parse_shape(shapes, event, current_presentation, current_slide, timestamp) - elsif eventname == 'UndoShapeEvent' or eventname == 'UndoAnnotationEvent' + elsif (eventname == 'UndoShapeEvent') || (eventname == 'UndoAnnotationEvent') events_parse_undo(shapes, event, current_presentation, current_slide, timestamp) - elsif eventname == 'ClearPageEvent' or eventname == 'ClearWhiteboardEvent' + elsif (eventname == 'ClearPageEvent') || (eventname == 'ClearWhiteboardEvent') events_parse_clear(shapes, event, current_presentation, current_slide, timestamp) elsif eventname == 'AssignPresenterEvent' @@ -943,7 +929,7 @@ def processPresentation(package_dir) elsif eventname == 'WhiteboardCursorMoveEvent' user_id = event.at_xpath('userId') # Only draw cursor for current presentor. TODO multi-cursor support - if user_id.nil? or user_id.text == presenter + if user_id.nil? || (user_id.text == presenter) cursor_x = event.at_xpath('xOffset').text.to_f cursor_y = event.at_xpath('yOffset').text.to_f cursor_visible = true @@ -954,14 +940,14 @@ def processPresentation(package_dir) # Perform slide finalization if slide_changed slide = slides.last - if !slide.nil? and - slide[:presentation] == current_presentation and - slide[:slide] == current_slide and - slide[:deskshare] == deskshare + if !slide.nil? && + (slide[:presentation] == current_presentation) && + (slide[:slide] == current_slide) && + (slide[:deskshare] == deskshare) BigBlueButton.logger.info('Presentation/Slide: skipping, no changes') slide_changed = false else - if !slide.nil? + unless slide.nil? slide[:out] = timestamp svg_render_image(svg, slide, shapes) end @@ -982,18 +968,18 @@ def processPresentation(package_dir) if panzoom_changed slide = slides.last panzoom = panzooms.last - if !panzoom.nil? and - panzoom[:x_offset] == current_x_offset and - panzoom[:y_offset] == current_y_offset and - panzoom[:width_ratio] == current_width_ratio and - panzoom[:height_ratio] == current_height_ratio and - panzoom[:width] == slide[:width] and - panzoom[:height] == slide[:height] and - panzoom[:deskshare] == deskshare + if !panzoom.nil? && + (panzoom[:x_offset] == current_x_offset) && + (panzoom[:y_offset] == current_y_offset) && + (panzoom[:width_ratio] == current_width_ratio) && + (panzoom[:height_ratio] == current_height_ratio) && + (panzoom[:width] == slide[:width]) && + (panzoom[:height] == slide[:height]) && + (panzoom[:deskshare] == deskshare) BigBlueButton.logger.info('Panzoom: skipping, no changes') panzoom_changed = false else - if !panzoom.nil? + unless panzoom.nil? panzoom[:out] = timestamp panzooms_emit_event(panzooms_rec, panzoom) end @@ -1006,41 +992,40 @@ def processPresentation(package_dir) width: slide[:width], height: slide[:height], in: timestamp, - deskshare: deskshare, + deskshare: deskshare } panzooms << panzoom end end # Perform cursor finalization - if cursor_changed or panzoom_changed - unless cursor_x >= 0 and cursor_x <= 100 and - cursor_y >= 0 and cursor_y <= 100 - cursor_visible = false - end + next unless cursor_changed || panzoom_changed + unless (cursor_x >= 0) && (cursor_x <= 100) && + cursor_y >= 0 && (cursor_y <= 100) + cursor_visible = false + end - panzoom = panzooms.last - cursor = cursors.last - if !cursor.nil? and - ((!cursor[:visible] and !cursor_visible) or - (cursor[:x] == cursor_x and cursor[:y] == cursor_y)) and - !panzoom_changed - BigBlueButton.logger.info('Cursor: skipping, no changes') - else - if !cursor.nil? - cursor[:out] = timestamp - cursors_emit_event(cursors_rec, cursor) - end - BigBlueButton.logger.info("Cursor: visible #{cursor_visible}, #{cursor_x} #{cursor_y} (#{panzoom[:width]}x#{panzoom[:height]})") - cursor = { - visible: cursor_visible, - x: cursor_x, - y: cursor_y, - panzoom: panzoom, - in: timestamp, - } - cursors << cursor + panzoom = panzooms.last + cursor = cursors.last + if !cursor.nil? && + ((!cursor[:visible] && !cursor_visible) || + ((cursor[:x] == cursor_x) && (cursor[:y] == cursor_y))) && + !panzoom_changed + BigBlueButton.logger.info('Cursor: skipping, no changes') + else + unless cursor.nil? + cursor[:out] = timestamp + cursors_emit_event(cursors_rec, cursor) end + BigBlueButton.logger.info("Cursor: visible #{cursor_visible}, #{cursor_x} #{cursor_y} (#{panzoom[:width]}x#{panzoom[:height]})") + cursor = { + visible: cursor_visible, + x: cursor_x, + y: cursor_y, + panzoom: panzoom, + in: timestamp + } + cursors << cursor end end @@ -1062,10 +1047,10 @@ def processPresentation(package_dir) end def processChatMessages(events, bbb_props) - BigBlueButton.logger.info("Processing chat events") + BigBlueButton.logger.info('Processing chat events') # Create slides.xml and chat. Nokogiri::XML::Builder.new do |xml| - xml.popcorn { + xml.popcorn do BigBlueButton::Events.get_chat_events(events, $meeting_start.to_i, $meeting_end.to_i, bbb_props).each do |chat| chattimeline = { in: (chat[:in] / 1000.0).round(1), @@ -1077,12 +1062,12 @@ def processChatMessages(events, bbb_props) chattimeline[:out] = (chat[:out] / 1000.0).round(1) unless chat[:out].nil? xml.chattimeline(**chattimeline) end - } + end end end def processDeskshareEvents(events) - BigBlueButton.logger.info("Processing deskshare events") + BigBlueButton.logger.info('Processing deskshare events') deskshare_matched_events = BigBlueButton::Events.get_matched_start_and_stop_deskshare_events(events) $deskshare_xml = Nokogiri::XML::Builder.new do |xml| @@ -1091,64 +1076,53 @@ def processDeskshareEvents(events) deskshare_matched_events.each do |event| start_timestamp = (translateTimestamp(event[:start_timestamp].to_f) / 1000).round(1) stop_timestamp = (translateTimestamp(event[:stop_timestamp].to_f) / 1000).round(1) - if (start_timestamp != stop_timestamp) - video_info = BigBlueButton::EDL::Video.video_info("#{$deskshare_dir}/#{event[:stream]}") - if !video_info[:video] - BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") - next - end - video_width, video_height = getDeskshareVideoDimension(event[:stream]) - $xml.event(:start_timestamp => start_timestamp, - :stop_timestamp => stop_timestamp, - :video_width => video_width, - :video_height => video_height) + next unless start_timestamp != stop_timestamp + video_info = BigBlueButton::EDL::Video.video_info("#{$deskshare_dir}/#{event[:stream]}") + unless video_info[:video] + BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") + next end + video_width, video_height = getDeskshareVideoDimension(event[:stream]) + $xml.event(start_timestamp: start_timestamp, + stop_timestamp: stop_timestamp, + video_width: video_width, + video_height: video_height) end end end end def getPollQuestion(event) - question = "" - if not event.at_xpath("question").nil? - question = event.at_xpath("question").text - end + question = '' + question = event.at_xpath('question').text unless event.at_xpath('question').nil? question end def getPollAnswers(event) answers = [] - if not event.at_xpath("answers").nil? - answers = JSON.load(event.at_xpath("answers").content) - end + answers = JSON.load(event.at_xpath('answers').content) unless event.at_xpath('answers').nil? answers end def getPollRespondents(event) respondents = 0 - if not event.at_xpath("numRespondents").nil? - respondents = event.at_xpath("numRespondents").text.to_i - end + respondents = event.at_xpath('numRespondents').text.to_i unless event.at_xpath('numRespondents').nil? respondents end def getPollResponders(event) responders = 0 - if not event.at_xpath("numResponders").nil? - responders = event.at_xpath("numResponders").text.to_i - end + responders = event.at_xpath('numResponders').text.to_i unless event.at_xpath('numResponders').nil? responders end def getPollId(event) - id = "" - if not event.at_xpath("pollId").nil? - id = event.at_xpath("pollId").text - end + id = '' + id = event.at_xpath('pollId').text unless event.at_xpath('pollId').nil? id end @@ -1156,12 +1130,12 @@ end def getPollType(events, published_poll_event) published_poll_id = getPollId(published_poll_event) - type = "" + type = '' events.xpath("//event[@eventname='PollStartedRecordEvent']").each do |event| poll_id = getPollId(event) if poll_id.eql?(published_poll_id) - type = event.at_xpath("type").text + type = event.at_xpath('type').text break end end @@ -1170,58 +1144,57 @@ def getPollType(events, published_poll_event) end def processPollEvents(events, package_dir) - BigBlueButton.logger.info("Processing poll events") + BigBlueButton.logger.info('Processing poll events') published_polls = [] $rec_events.each do |re| events.xpath("//event[@eventname='PollPublishedRecordEvent']").each do |event| - if (event[:timestamp].to_i >= re[:start_timestamp] and event[:timestamp].to_i <= re[:stop_timestamp]) - published_polls << { - :timestamp => (translateTimestamp(event[:timestamp]) / 1000).to_i, - :type => getPollType(events, event), - :question => getPollQuestion(event), - :answers => getPollAnswers(event), - :respondents => getPollRespondents(event), - :responders => getPollResponders(event) - } - end + next unless (event[:timestamp].to_i >= re[:start_timestamp]) && (event[:timestamp].to_i <= re[:stop_timestamp]) + published_polls << { + timestamp: (translateTimestamp(event[:timestamp]) / 1000).to_i, + type: getPollType(events, event), + question: getPollQuestion(event), + answers: getPollAnswers(event), + respondents: getPollRespondents(event), + responders: getPollResponders(event) + } end end - if not published_polls.empty? - File.open("#{package_dir}/polls.json", "w") do |f| + unless published_polls.empty? + File.open("#{package_dir}/polls.json", 'w') do |f| f.puts(published_polls.to_json) end end end -def processExternalVideoEvents(events, package_dir) - BigBlueButton.logger.info("Processing external video events") +def processExternalVideoEvents(_events, package_dir) + BigBlueButton.logger.info('Processing external video events') # Retrieve external video events external_video_events = BigBlueButton::Events.match_start_and_stop_external_video_events( - BigBlueButton::Events.get_start_and_stop_external_video_events(@doc)) + BigBlueButton::Events.get_start_and_stop_external_video_events(@doc) + ) external_videos = [] $rec_events.each do |re| external_video_events.each do |event| - #BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}") + # BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}") timestamp = (translateTimestamp(event[:start_timestamp]) / 1000).to_i # do not add same external_video twice - if (external_videos.find {|ev| ev[:timestamp] == timestamp}.nil?) - if ((event[:start_timestamp] >= re[:start_timestamp] and event[:start_timestamp] <= re[:stop_timestamp]) || - (event[:start_timestamp] < re[:start_timestamp] and event[:stop_timestamp] >= re[:start_timestamp])) - external_videos << { - :timestamp => timestamp, - :external_video_url => event[:external_video_url] - } - end + next unless external_videos.find { |ev| ev[:timestamp] == timestamp }.nil? + if ((event[:start_timestamp] >= re[:start_timestamp]) && (event[:start_timestamp] <= re[:stop_timestamp])) || + ((event[:start_timestamp] < re[:start_timestamp]) && (event[:stop_timestamp] >= re[:start_timestamp])) + external_videos << { + timestamp: timestamp, + external_video_url: event[:external_video_url] + } end end end - if not external_videos.empty? - File.open("#{package_dir}/external_videos.json", "w") do |f| + unless external_videos.empty? + File.open("#{package_dir}/external_videos.json", 'w') do |f| f.puts(external_videos.to_json) end end @@ -1232,8 +1205,8 @@ $panzooms_xml_filename = 'panzooms.xml' $cursor_xml_filename = 'cursor.xml' $deskshare_xml_filename = 'deskshare.xml' -opts = Trollop::options do - opt :meeting_id, "Meeting id to archive", :default => '58f4a6b3-cd07-444d-8564-59116cb53974', :type => String +opts = Trollop.options do + opt :meeting_id, 'Meeting id to archive', default: '58f4a6b3-cd07-444d-8564-59116cb53974', type: String end $meeting_id = opts[:meeting_id] @@ -1246,42 +1219,40 @@ puts $meeting_id puts $playback begin - - if ($playback == "presentation") + if $playback == 'presentation' log_dir = bbb_props['log_dir'] - logger = Logger.new("#{log_dir}/presentation/publish-#{$meeting_id}.log", 'daily' ) + logger = Logger.new("#{log_dir}/presentation/publish-#{$meeting_id}.log", 'daily') BigBlueButton.logger = logger - BigBlueButton.logger.info("Setting recording dir") + BigBlueButton.logger.info('Setting recording dir') recording_dir = bbb_props['recording_dir'] - BigBlueButton.logger.info("Setting process dir") + BigBlueButton.logger.info('Setting process dir') $process_dir = "#{recording_dir}/process/presentation/#{$meeting_id}" - BigBlueButton.logger.info("setting publish dir") + BigBlueButton.logger.info('setting publish dir') publish_dir = $presentation_props['publish_dir'] - BigBlueButton.logger.info("setting playback url info") + BigBlueButton.logger.info('setting playback url info') playback_protocol = bbb_props['playback_protocol'] playback_host = bbb_props['playback_host'] - BigBlueButton.logger.info("setting target dir") + BigBlueButton.logger.info('setting target dir') target_dir = "#{recording_dir}/publish/presentation/#{$meeting_id}" $deskshare_dir = "#{recording_dir}/raw/#{$meeting_id}/deskshare" - if not FileTest.directory?(target_dir) - BigBlueButton.logger.info("Making dir target_dir") + if !FileTest.directory?(target_dir) + BigBlueButton.logger.info('Making dir target_dir') FileUtils.mkdir_p target_dir package_dir = "#{target_dir}/#{$meeting_id}" - BigBlueButton.logger.info("Making dir package_dir") + BigBlueButton.logger.info('Making dir package_dir') FileUtils.mkdir_p package_dir begin - video_formats = $presentation_props['video_formats'] video_files = Dir.glob("#{$process_dir}/webcams.{#{video_formats.join(',')}}") - if ! video_files.empty? - BigBlueButton.logger.info("Making video dir") + if !video_files.empty? + BigBlueButton.logger.info('Making video dir') video_dir = "#{package_dir}/video" FileUtils.mkdir_p video_dir video_files.each do |video_file| @@ -1291,17 +1262,17 @@ begin end else audio_dir = "#{package_dir}/audio" - BigBlueButton.logger.info("Making audio dir") + BigBlueButton.logger.info('Making audio dir') FileUtils.mkdir_p audio_dir BigBlueButton.logger.info("Made audio dir - copying: #{$process_dir}/audio.webm to -> #{audio_dir}") FileUtils.cp("#{$process_dir}/audio.webm", audio_dir) BigBlueButton.logger.info("Copied audio.webm file - copying: #{$process_dir}/audio.ogg to -> #{audio_dir}") FileUtils.cp("#{$process_dir}/audio.ogg", audio_dir) - BigBlueButton.logger.info("Copied audio.ogg file") + BigBlueButton.logger.info('Copied audio.ogg file') end if File.exist?("#{$process_dir}/captions.json") - BigBlueButton.logger.info("Copying caption files") + BigBlueButton.logger.info('Copying caption files') FileUtils.cp("#{$process_dir}/captions.json", package_dir) Dir.glob("#{$process_dir}/caption_*.vtt").each do |caption| BigBlueButton.logger.debug(caption) @@ -1310,8 +1281,8 @@ begin end video_files = Dir.glob("#{$process_dir}/deskshare.{#{video_formats.join(',')}}") - if ! video_files.empty? - BigBlueButton.logger.info("Making deskshare dir") + if !video_files.empty? + BigBlueButton.logger.info('Making deskshare dir') deskshare_dir = "#{package_dir}/deskshare" FileUtils.mkdir_p deskshare_dir video_files.each do |video_file| @@ -1327,9 +1298,7 @@ begin FileUtils.cp("#{$process_dir}/presentation_text.json", package_dir) end - if File.exist?("#{$process_dir}/notes/notes.html") - FileUtils.cp("#{$process_dir}/notes/notes.html", package_dir) - end + FileUtils.cp("#{$process_dir}/notes/notes.html", package_dir) if File.exist?("#{$process_dir}/notes/notes.html") processing_time = File.read("#{$process_dir}/processing_time") @@ -1337,20 +1306,23 @@ begin # Retrieve record events and calculate total recording duration. $rec_events = BigBlueButton::Events.match_start_and_stop_rec_events( - BigBlueButton::Events.get_start_and_stop_rec_events(@doc)) + BigBlueButton::Events.get_start_and_stop_rec_events(@doc) + ) recording_time = BigBlueButton::Events.get_recording_length(@doc) # presentation_url = "/slides/" + $meeting_id + "/presentation" - $meeting_start = @doc.xpath("//event")[0][:timestamp] - $meeting_end = @doc.xpath("//event").last()[:timestamp] + $meeting_start = @doc.xpath('//event')[0][:timestamp] + $meeting_end = @doc.xpath('//event').last[:timestamp] $version_atleast_0_9_0 = BigBlueButton::Events.bbb_version_compare( - @doc, 0, 9, 0) + @doc, 0, 9, 0 + ) $version_atleast_2_0_0 = BigBlueButton::Events.bbb_version_compare( - @doc, 2, 0, 0) - BigBlueButton.logger.info("Creating metadata.xml") + @doc, 2, 0, 0 + ) + BigBlueButton.logger.info('Creating metadata.xml') # Get the real-time start and end timestamp match = /.*-(\d+)$/.match($meeting_id) @@ -1360,54 +1332,52 @@ begin #### INSTEAD OF CREATING THE WHOLE metadata.xml FILE AGAIN, ONLY ADD # Copy metadata.xml from process_dir FileUtils.cp("#{$process_dir}/metadata.xml", package_dir) - BigBlueButton.logger.info("Copied metadata.xml file") + BigBlueButton.logger.info('Copied metadata.xml file') # Update state and add playback to metadata.xml ## Load metadata.xml metadata = Nokogiri::XML(File.read("#{package_dir}/metadata.xml")) ## Update state recording = metadata.root - state = recording.at_xpath("state") - state.content = "published" - published = recording.at_xpath("published") - published.content = "true" + state = recording.at_xpath('state') + state.content = 'published' + published = recording.at_xpath('published') + published.content = 'true' ## Remove empty playback - metadata.search('//recording/playback').each do |playback| - playback.remove - end + metadata.search('//recording/playback').each(&:remove) ## Add the actual playback - presentation = BigBlueButton::Presentation.get_presentation_for_preview("#{$process_dir}") + presentation = BigBlueButton::Presentation.get_presentation_for_preview($process_dir.to_s) metadata_with_playback = Nokogiri::XML::Builder.with(metadata.at('recording')) do |xml| - xml.playback { - xml.format("presentation") - xml.link("#{playback_protocol}://#{playback_host}/playback/presentation/2.3/#{$meeting_id}") - xml.processing_time("#{processing_time}") - xml.duration("#{recording_time}") - unless presentation.empty? - xml.extensions { - xml.preview { - xml.images { - presentation[:slides].each do |key,val| - attributes = {:width => "176", :height => "136", :alt => (val[:alt] != nil)? "#{val[:alt]}": ""} - xml.image(attributes){ xml.text("#{playback_protocol}://#{playback_host}/presentation/#{$meeting_id}/presentation/#{presentation[:id]}/thumbnails/thumb-#{key}.png") } - end - } - } - } + xml.playback do + xml.format('presentation') + xml.link("#{playback_protocol}://#{playback_host}/playback/presentation/2.3/#{$meeting_id}") + xml.processing_time(processing_time.to_s) + xml.duration(recording_time.to_s) + unless presentation.empty? + xml.extensions do + xml.preview do + xml.images do + presentation[:slides].each do |key, val| + attributes = { width: '176', height: '136', alt: !val[:alt].nil? ? (val[:alt]).to_s : '' } + xml.image(attributes) { xml.text("#{playback_protocol}://#{playback_host}/presentation/#{$meeting_id}/presentation/#{presentation[:id]}/thumbnails/thumb-#{key}.png") } + end + end + end end - } + end + end end ## Write the new metadata.xml - metadata_file = File.new("#{package_dir}/metadata.xml","w") - metadata = Nokogiri::XML(metadata.to_xml) { |x| x.noblanks } + metadata_file = File.new("#{package_dir}/metadata.xml", 'w') + metadata = Nokogiri::XML(metadata.to_xml, &:noblanks) metadata_file.write(metadata.root) metadata_file.close - BigBlueButton.logger.info("Added playback to metadata.xml") + BigBlueButton.logger.info('Added playback to metadata.xml') - #Create slides.xml - BigBlueButton.logger.info("Generating xml for slides and chat") + # Create slides.xml + BigBlueButton.logger.info('Generating xml for slides and chat') - calculateRecordEventsOffset() + calculateRecordEventsOffset # Write slides.xml to file slides_doc = processChatMessages(@doc, bbb_props) @@ -1424,15 +1394,13 @@ begin # Write deskshare.xml to file File.open("#{package_dir}/#{$deskshare_xml_filename}", 'w') { |f| f.puts $deskshare_xml.to_xml } - BigBlueButton.logger.info("Copying files to package dir") + BigBlueButton.logger.info('Copying files to package dir') FileUtils.cp_r("#{$process_dir}/presentation", package_dir) - BigBlueButton.logger.info("Copied files to package dir") + BigBlueButton.logger.info('Copied files to package dir') - BigBlueButton.logger.info("Publishing slides") + BigBlueButton.logger.info('Publishing slides') # Now publish this recording files by copying them into the publish folder. - if not FileTest.directory?(publish_dir) - FileUtils.mkdir_p publish_dir - end + FileUtils.mkdir_p publish_dir unless FileTest.directory?(publish_dir) # Get raw size of presentation files raw_dir = "#{recording_dir}/raw/#{$meeting_id}" @@ -1441,21 +1409,22 @@ begin BigBlueButton.add_playback_size_to_metadata(package_dir) FileUtils.cp_r(package_dir, publish_dir) # Copy all the files. - BigBlueButton.logger.info("Finished publishing script presentation.rb successfully.") + BigBlueButton.logger.info('Finished publishing script presentation.rb successfully.') - BigBlueButton.logger.info("Removing processed files.") - FileUtils.rm_r(Dir.glob("#{$process_dir}/*")) + # TODO: remove comments + # BigBlueButton.logger.info("Removing processed files.") + # FileUtils.rm_r(Dir.glob("#{$process_dir}/*")) - BigBlueButton.logger.info("Removing published files.") + BigBlueButton.logger.info('Removing published files.') FileUtils.rm_r(Dir.glob("#{target_dir}/*")) - rescue Exception => e - BigBlueButton.logger.error(e.message) - e.backtrace.each do |traceline| + rescue Exception => e + BigBlueButton.logger.error(e.message) + e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) end exit 1 end - publish_done = File.new("#{recording_dir}/status/published/#{$meeting_id}-presentation.done", "w") + publish_done = File.new("#{recording_dir}/status/published/#{$meeting_id}-presentation.done", 'w') publish_done.write("Published #{$meeting_id}") publish_done.close @@ -1463,14 +1432,12 @@ begin BigBlueButton.logger.info("#{target_dir} is already there") end end - - rescue Exception => e BigBlueButton.logger.error(e.message) e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) end - publish_done = File.new("#{recording_dir}/status/published/#{$meeting_id}-presentation.fail", "w") + publish_done = File.new("#{recording_dir}/status/published/#{$meeting_id}-presentation.fail", 'w') publish_done.write("Failed Publishing #{$meeting_id}") publish_done.close From a687ab320d436fac661d985d6644c4e3c17667fb Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sun, 5 Dec 2021 16:09:26 +0100 Subject: [PATCH 03/26] Fix Q_CURVE_TO annotation path --- .../presentation/scripts/publish/presentation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 00b7eafa80..5c1e477895 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -174,7 +174,7 @@ def svg_render_shape_pencil(g, slide, shape) cy1 = shape_scale_height(slide, data_points.next) x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) - path.push("Q#{cx1} #{cy2},#{x} #{y}") + path.push("Q#{cx1} #{cy1},#{x} #{y}") when 4 # C_CURVE_TO cx1 = shape_scale_width(slide, data_points.next) cy1 = shape_scale_height(slide, data_points.next) From 1d9146a101d82357e1066cc1380c2a8d535e718a Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sun, 5 Dec 2021 19:04:21 +0100 Subject: [PATCH 04/26] Chore: remove useless variables and more + Use guard clauses where applicable + Use JSON parse over JSON load + Rescue StandardError instead of Exception + Formatting / Stylistic choices json parse over load --- .../scripts/publish/presentation.rb | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 5c1e477895..563bccc125 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -20,7 +20,7 @@ # with BigBlueButton; if not, see . # -performance_start = Time.now +_performance_start = Time.now # For DEVELOPMENT # Allows us to run the script manually @@ -103,13 +103,12 @@ end def translateTimestamp_helper(timestamp) $rec_events.each do |event| # if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording - if timestamp <= event[:start_timestamp] - return event[:start_timestamp] - event[:offset] + return event[:start_timestamp] - event[:offset] if timestamp <= event[:start_timestamp] + # if the timestamp is during the recording period, it is just translated to the new one using the offset - elsif (timestamp > event[:start_timestamp]) && (timestamp <= event[:stop_timestamp]) - return timestamp - event[:offset] - end + return timestamp - event[:offset] if (timestamp > event[:start_timestamp]) && (timestamp <= event[:stop_timestamp]) end + # if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp timestamp - $rec_events.last[:offset] + $rec_events.last[:duration] end @@ -245,7 +244,7 @@ def svg_render_shape_rect(g, slide, shape) y2 = shape_scale_height(slide, data_points[3]) width = (x2 - x1).abs - height = (y2 - y1).abs + # height = (y2 - y1).abs if shape[:square] # Convert to a square, keeping aligned with the start point. @@ -353,7 +352,7 @@ def svg_render_shape_text(g, slide, shape) p = doc.create_element('p', xmlns: 'http://www.w3.org/1999/xhtml', style: 'margin:0;padding:0') shape[:text].each_line.with_index do |line, index| - p << doc.create_element('br') if index > 0 + p << doc.create_element('br') if index.positive? p << doc.create_text_node(line.chomp) end fo << p @@ -529,7 +528,7 @@ def cursors_emit_event(rec, cursor) (panzoom[:width_ratio] / 100.0)).round(5) y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * $magic_mystery_number / 100.0)) / (panzoom[:height_ratio] / 100.0)).round(5) - if (x < 0) || (x > 1) || (y < 0) || (y > 1) + if x.negative? || (x > 1) || y.negative? || (y > 1) x = -1.0 y = -1.0 end @@ -945,7 +944,7 @@ def processPresentation(package_dir) (slide[:slide] == current_slide) && (slide[:deskshare] == deskshare) BigBlueButton.logger.info('Presentation/Slide: skipping, no changes') - slide_changed = false + # slide_changed = false else unless slide.nil? slide[:out] = timestamp @@ -1101,7 +1100,7 @@ end def getPollAnswers(event) answers = [] - answers = JSON.load(event.at_xpath('answers').content) unless event.at_xpath('answers').nil? + answers = JSON.parse(event.at_xpath('answers').content) unless event.at_xpath('answers').nil? answers end @@ -1161,11 +1160,7 @@ def processPollEvents(events, package_dir) end end - unless published_polls.empty? - File.open("#{package_dir}/polls.json", 'w') do |f| - f.puts(published_polls.to_json) - end - end + File.open("#{package_dir}/polls.json", 'w') { |f| f.puts(published_polls.to_json) } unless published_polls.empty? end def processExternalVideoEvents(_events, package_dir) @@ -1325,9 +1320,9 @@ begin BigBlueButton.logger.info('Creating metadata.xml') # Get the real-time start and end timestamp - match = /.*-(\d+)$/.match($meeting_id) - real_start_time = match[1] - real_end_time = (real_start_time.to_i + ($meeting_end.to_i - $meeting_start.to_i)).to_s + # match = /.*-(\d+)$/.match($meeting_id) + # real_start_time = match[1] + # real_end_time = (real_start_time.to_i + ($meeting_end.to_i - $meeting_start.to_i)).to_s #### INSTEAD OF CREATING THE WHOLE metadata.xml FILE AGAIN, ONLY ADD # Copy metadata.xml from process_dir @@ -1347,7 +1342,7 @@ begin metadata.search('//recording/playback').each(&:remove) ## Add the actual playback presentation = BigBlueButton::Presentation.get_presentation_for_preview($process_dir.to_s) - metadata_with_playback = Nokogiri::XML::Builder.with(metadata.at('recording')) do |xml| + Nokogiri::XML::Builder.with(metadata.at('recording')) do |xml| xml.playback do xml.format('presentation') xml.link("#{playback_protocol}://#{playback_host}/playback/presentation/2.3/#{$meeting_id}") @@ -1417,7 +1412,7 @@ begin BigBlueButton.logger.info('Removing published files.') FileUtils.rm_r(Dir.glob("#{target_dir}/*")) - rescue Exception => e + rescue StandardError => e BigBlueButton.logger.error(e.message) e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) @@ -1432,7 +1427,7 @@ begin BigBlueButton.logger.info("#{target_dir} is already there") end end -rescue Exception => e +rescue StandardError => e BigBlueButton.logger.error(e.message) e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) From 971aeb9e3d2033d809aceb5a6815acfc1a32964b Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sun, 5 Dec 2021 19:35:22 +0100 Subject: [PATCH 05/26] Use instance variables instead of global variables --- .../scripts/publish/presentation.rb | 208 +++++++++--------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 563bccc125..9c1440a450 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -38,15 +38,15 @@ require 'json' # This script lives in scripts/archive/steps while properties.yaml lives in scripts/ bbb_props = BigBlueButton.read_props -$presentation_props = YAML.safe_load(File.read('presentation.yml')) +@presentation_props = YAML.safe_load(File.read('presentation.yml')) # There's a couple of places where stuff is mysteriously divided or multiplied # by 2. This is just here to call out how spooky that is. -$magic_mystery_number = 2 +@magic_mystery_number = 2 def scaleToDeskshareVideo(width, height) - deskshare_video_height = $presentation_props['deskshare_output_height'].to_f - deskshare_video_width = $presentation_props['deskshare_output_height'].to_f + deskshare_video_height = @presentation_props['deskshare_output_height'].to_f + deskshare_video_width = @presentation_props['deskshare_output_height'].to_f scale = [deskshare_video_width / width, deskshare_video_height / height] video_width = width * scale.min @@ -56,9 +56,9 @@ def scaleToDeskshareVideo(width, height) end def getDeskshareVideoDimension(deskshare_stream_name) - video_width = $presentation_props['deskshare_output_height'].to_f - video_height = $presentation_props['deskshare_output_height'].to_f - deskshare_video_filename = "#{$deskshare_dir}/#{deskshare_stream_name}" + video_width = @presentation_props['deskshare_output_height'].to_f + video_height = @presentation_props['deskshare_output_height'].to_f + deskshare_video_filename = "#{@deskshare_dir}/#{deskshare_stream_name}" if File.exist?(deskshare_video_filename) video_info = BigBlueButton::EDL::Video.video_info(deskshare_video_filename) @@ -76,8 +76,8 @@ end # def calculateRecordEventsOffset accumulated_duration = 0 - previous_stop_recording = $meeting_start.to_f - $rec_events.each do |event| + previous_stop_recording = @meeting_start.to_f + @rec_events.each do |event| event[:offset] = event[:start_timestamp] - accumulated_duration event[:duration] = event[:stop_timestamp] - event[:start_timestamp] event[:accumulated_duration] = accumulated_duration @@ -93,7 +93,7 @@ end # def translateTimestamp(timestamp) new_timestamp = translateTimestamp_helper(timestamp.to_f).to_f - # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-$meeting_start.to_f}, new value=#{new_timestamp}") + # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-@meeting_start.to_f}, new value=#{new_timestamp}") new_timestamp end @@ -101,7 +101,7 @@ end # Translated an arbitrary Unix timestamp to the recording timestamp # def translateTimestamp_helper(timestamp) - $rec_events.each do |event| + @rec_events.each do |event| # if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording return event[:start_timestamp] - event[:offset] if timestamp <= event[:start_timestamp] @@ -110,7 +110,7 @@ def translateTimestamp_helper(timestamp) end # if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp - timestamp - $rec_events.last[:offset] + $rec_events.last[:duration] + timestamp - @rec_events.last[:offset] + @rec_events.last[:duration] end def color_to_hex(color) @@ -211,7 +211,7 @@ end def svg_render_shape_line(g, slide, shape) g['shape'] = "line#{shape[:shape_unique_id]}" g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none" - g['style'] += if $version_atleast_2_0_0 + g['style'] += if @version_atleast_2_0_0 ';stroke-linecap:butt' else ';stroke-linecap:round' @@ -230,7 +230,7 @@ end def svg_render_shape_rect(g, slide, shape) g['shape'] = "rect#{shape[:shape_unique_id]}" g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" - g['style'] += if $version_atleast_2_0_0 + g['style'] += if @version_atleast_2_0_0 ';stroke-linejoin:miter' else ';stroke-linejoin:round' @@ -265,7 +265,7 @@ end def svg_render_shape_triangle(g, slide, shape) g['shape'] = "triangle#{shape[:shape_unique_id]}" g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" - g['style'] += if $version_atleast_2_0_0 + g['style'] += if @version_atleast_2_0_0 ';stroke-linejoin:miter;stroke-miterlimit:8' else ';stroke-linejoin:round' @@ -376,8 +376,8 @@ def svg_render_shape_poll(g, slide, shape) num_responders = shape[:num_responders] presentation = slide[:presentation] - json_file = "#{$process_dir}/poll_result#{poll_id}.json" - svg_file = "#{$process_dir}/presentation/#{presentation}/poll_result#{poll_id}.svg" + json_file = "#{@process_dir}/poll_result#{poll_id}.json" + svg_file = "#{@process_dir}/presentation/#{presentation}/poll_result#{poll_id}.svg" # Save the poll json to a temp file IO.write(json_file, result) @@ -434,15 +434,15 @@ def svg_render_shape(canvas, slide, shape, image_id) canvas << g unless g.element_children.empty? end -$svg_image_id = 1 +@svg_image_id = 1 def svg_render_image(svg, slide, shapes) if slide[:in] == slide[:out] BigBlueButton.logger.info("Presentation #{slide[:presentation]} Slide #{slide[:slide]} is never shown (duration rounds to 0)") return end - image_id = $svg_image_id - $svg_image_id += 1 + image_id = @svg_image_id + @svg_image_id += 1 BigBlueButton.logger.info("Image #{image_id}: Presentation #{slide[:presentation]} Slide #{slide[:slide]} Deskshare #{slide[:deskshare]} from #{slide[:in]} to #{slide[:out]}") @@ -482,8 +482,8 @@ def panzoom_viewbox(panzoom) panzoom[:height_ratio] = 100.0 end - x = (-panzoom[:x_offset] * $magic_mystery_number / 100.0 * panzoom[:width]).round(5) - y = (-panzoom[:y_offset] * $magic_mystery_number / 100.0 * panzoom[:height]).round(5) + x = (-panzoom[:x_offset] * @magic_mystery_number / 100.0 * panzoom[:width]).round(5) + y = (-panzoom[:y_offset] * @magic_mystery_number / 100.0 * panzoom[:height]).round(5) w = shape_scale_width(panzoom, panzoom[:width_ratio]) h = shape_scale_height(panzoom, panzoom[:height_ratio]) @@ -521,12 +521,12 @@ def cursors_emit_event(rec, cursor) panzoom = cursor[:panzoom] if cursor[:visible] - if $version_atleast_2_0_0 + if @version_atleast_2_0_0 # In BBB 2.0, the cursor now uses the same coordinate system as annotations # Use the panzoom information to convert it to be relative to viewbox - x = (((cursor[:x] / 100.0) + (panzoom[:x_offset] * $magic_mystery_number / 100.0)) / + x = (((cursor[:x] / 100.0) + (panzoom[:x_offset] * @magic_mystery_number / 100.0)) / (panzoom[:width_ratio] / 100.0)).round(5) - y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * $magic_mystery_number / 100.0)) / + y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * @magic_mystery_number / 100.0)) / (panzoom[:height_ratio] / 100.0)).round(5) if x.negative? || (x > 1) || y.negative? || (y > 1) x = -1.0 @@ -551,8 +551,8 @@ def cursors_emit_event(rec, cursor) rec << event end -$svg_shape_id = 1 -$svg_shape_unique_id = 1 +@svg_shape_id = 1 +@svg_shape_unique_id = 1 def events_parse_shape(shapes, event, current_presentation, current_slide, timestamp) # Figure out what presentation+slide this shape is for, with fallbacks # for old BBB where this info isn't in the shape messages @@ -567,7 +567,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times slide = current_slide else slide = slide.text.to_i - slide -= 1 unless $version_atleast_0_9_0 + slide -= 1 unless @version_atleast_0_9_0 end # Set up the shapes data structures if needed @@ -590,8 +590,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:id] = shape_id.text unless shape_id.nil? status = event.at_xpath('status') shape[:status] = status.text unless status.nil? - shape[:shape_id] = $svg_shape_id - $svg_shape_id += 1 + shape[:shape_id] = @svg_shape_id + @svg_shape_id += 1 # Some shape-specific properties if (shape[:type] == 'pencil') || (shape[:type] == 'rectangle') || @@ -603,7 +603,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times BigBlueButton.logger.warn("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} is missing thickness") return end - if $version_atleast_2_0_0 + if @version_atleast_2_0_0 shape[:thickness_percent] = thickness.text.to_f else shape[:thickness] = thickness.text.to_i @@ -679,8 +679,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:data_points] = prev_shape[:data_points] + shape[:data_points] end else - shape[:shape_unique_id] = $svg_shape_unique_id - $svg_shape_unique_id += 1 + shape[:shape_unique_id] = @svg_shape_unique_id + @svg_shape_unique_id += 1 end BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} Type #{shape[:type]}") @@ -701,7 +701,7 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest slide = current_slide else slide = slide.text.to_i - slide -= 1 unless $version_atleast_0_9_0 + slide -= 1 unless @version_atleast_0_9_0 end # Newer undo messages have the shape id, making this a lot easier shape_id = event.at_xpath('shapeId') @@ -755,7 +755,7 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times slide = current_slide else slide = slide.text.to_i - slide -= 1 unless $version_atleast_0_9_0 + slide -= 1 unless @version_atleast_0_9_0 end # BigBlueButton 2.0 per-user clear features @@ -798,7 +798,7 @@ def events_get_image_info(slide) slide[:src] = "presentation/#{slide[:presentation]}/slide-#{slide[:slide] + 1}.png" slide[:text] = "presentation/#{slide[:presentation]}/textfiles/slide-#{slide[:slide] + 1}.txt" end - image_path = "#{$process_dir}/#{slide[:src]}" + image_path = "#{@process_dir}/#{slide[:src]}" unless File.exist?(image_path) BigBlueButton.logger.warn("Missing image file #{image_path}!") @@ -806,7 +806,7 @@ def events_get_image_info(slide) FileUtils.mkdir_p(File.dirname(image_path)) command = \ if slide[:deskshare] - ['convert', '-size', "#{$presentation_props['deskshare_output_width']}x#{$presentation_props['deskshare_output_height']}", 'xc:transparent', '-background', 'transparent', image_path] + ['convert', '-size', "#{@presentation_props['deskshare_output_width']}x#{@presentation_props['deskshare_output_height']}", 'xc:transparent', '-background', 'transparent', image_path] else ['convert', '-size', '1600x1200', 'xc:transparent', '-background', 'transparent', '-quality', '90', '+dither', '-depth', '8', '-colors', '256', image_path] end @@ -864,7 +864,7 @@ def processPresentation(package_dir) # Iterate through the events.xml and store the events, building the # xml files as we go last_timestamp = 0.0 - events_xml = Nokogiri::XML(File.read("#{$process_dir}/events.xml")) + events_xml = Nokogiri::XML(File.read("#{@process_dir}/events.xml")) events_xml.xpath('/recording/event').each do |event| eventname = event['eventname'] last_timestamp = timestamp = @@ -895,11 +895,11 @@ def processPresentation(package_dir) current_height_ratio = event.at_xpath('heightRatio').text.to_f panzoom_changed = true - elsif $presentation_props['include_deskshare'] && ((eventname == 'DeskshareStartedEvent') || (eventname == 'StartWebRTCDesktopShareEvent')) + elsif @presentation_props['include_deskshare'] && ((eventname == 'DeskshareStartedEvent') || (eventname == 'StartWebRTCDesktopShareEvent')) deskshare = true slide_changed = true - elsif $presentation_props['include_deskshare'] && ((eventname == 'DeskshareStoppedEvent') || (eventname == 'StopWebRTCDesktopShareEvent')) + elsif @presentation_props['include_deskshare'] && ((eventname == 'DeskshareStoppedEvent') || (eventname == 'StopWebRTCDesktopShareEvent')) deskshare = false slide_changed = true @@ -1040,9 +1040,9 @@ def processPresentation(package_dir) cursors_emit_event(cursors_rec, cursor) # And save the result - File.write("#{package_dir}/#{$shapes_svg_filename}", shapes_doc.to_xml) - File.write("#{package_dir}/#{$panzooms_xml_filename}", panzooms_doc.to_xml) - File.write("#{package_dir}/#{$cursor_xml_filename}", cursors_doc.to_xml) + File.write("#{package_dir}/#{@shapes_svg_filename}", shapes_doc.to_xml) + File.write("#{package_dir}/#{@panzooms_xml_filename}", panzooms_doc.to_xml) + File.write("#{package_dir}/#{@cursor_xml_filename}", cursors_doc.to_xml) end def processChatMessages(events, bbb_props) @@ -1050,7 +1050,7 @@ def processChatMessages(events, bbb_props) # Create slides.xml and chat. Nokogiri::XML::Builder.new do |xml| xml.popcorn do - BigBlueButton::Events.get_chat_events(events, $meeting_start.to_i, $meeting_end.to_i, bbb_props).each do |chat| + BigBlueButton::Events.get_chat_events(events, @meeting_start.to_i, @meeting_end.to_i, bbb_props).each do |chat| chattimeline = { in: (chat[:in] / 1000.0).round(1), direction: 'down', @@ -1069,20 +1069,20 @@ def processDeskshareEvents(events) BigBlueButton.logger.info('Processing deskshare events') deskshare_matched_events = BigBlueButton::Events.get_matched_start_and_stop_deskshare_events(events) - $deskshare_xml = Nokogiri::XML::Builder.new do |xml| - $xml = xml - $xml.recording('id' => 'deskshare_events') do + @deskshare_xml = Nokogiri::XML::Builder.new do |xml| + @xml = xml + @xml.recording('id' => 'deskshare_events') do deskshare_matched_events.each do |event| start_timestamp = (translateTimestamp(event[:start_timestamp].to_f) / 1000).round(1) stop_timestamp = (translateTimestamp(event[:stop_timestamp].to_f) / 1000).round(1) next unless start_timestamp != stop_timestamp - video_info = BigBlueButton::EDL::Video.video_info("#{$deskshare_dir}/#{event[:stream]}") + video_info = BigBlueButton::EDL::Video.video_info("#{@deskshare_dir}/#{event[:stream]}") unless video_info[:video] BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") next end video_width, video_height = getDeskshareVideoDimension(event[:stream]) - $xml.event(start_timestamp: start_timestamp, + @xml.event(start_timestamp: start_timestamp, stop_timestamp: stop_timestamp, video_width: video_width, video_height: video_height) @@ -1146,7 +1146,7 @@ def processPollEvents(events, package_dir) BigBlueButton.logger.info('Processing poll events') published_polls = [] - $rec_events.each do |re| + @rec_events.each do |re| events.xpath("//event[@eventname='PollPublishedRecordEvent']").each do |event| next unless (event[:timestamp].to_i >= re[:start_timestamp]) && (event[:timestamp].to_i <= re[:stop_timestamp]) published_polls << { @@ -1172,7 +1172,7 @@ def processExternalVideoEvents(_events, package_dir) ) external_videos = [] - $rec_events.each do |re| + @rec_events.each do |re| external_video_events.each do |event| # BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}") timestamp = (translateTimestamp(event[:start_timestamp]) / 1000).to_i @@ -1195,57 +1195,57 @@ def processExternalVideoEvents(_events, package_dir) end end -$shapes_svg_filename = 'shapes.svg' -$panzooms_xml_filename = 'panzooms.xml' -$cursor_xml_filename = 'cursor.xml' -$deskshare_xml_filename = 'deskshare.xml' +@shapes_svg_filename = 'shapes.svg' +@panzooms_xml_filename = 'panzooms.xml' +@cursor_xml_filename = 'cursor.xml' +@deskshare_xml_filename = 'deskshare.xml' opts = Trollop.options do opt :meeting_id, 'Meeting id to archive', default: '58f4a6b3-cd07-444d-8564-59116cb53974', type: String end -$meeting_id = opts[:meeting_id] -puts $meeting_id -match = /(.*)-(.*)/.match $meeting_id -$meeting_id = match[1] -$playback = match[2] +@meeting_id = opts[:meeting_id] +puts @meeting_id +match = /(.*)-(.*)/.match @meeting_id +@meeting_id = match[1] +@playback = match[2] -puts $meeting_id -puts $playback +puts @meeting_id +puts @playback begin - if $playback == 'presentation' + if @playback == 'presentation' log_dir = bbb_props['log_dir'] - logger = Logger.new("#{log_dir}/presentation/publish-#{$meeting_id}.log", 'daily') + logger = Logger.new("#{log_dir}/presentation/publish-#{@meeting_id}.log", 'daily') BigBlueButton.logger = logger BigBlueButton.logger.info('Setting recording dir') recording_dir = bbb_props['recording_dir'] BigBlueButton.logger.info('Setting process dir') - $process_dir = "#{recording_dir}/process/presentation/#{$meeting_id}" + @process_dir = "#{recording_dir}/process/presentation/#{@meeting_id}" BigBlueButton.logger.info('setting publish dir') - publish_dir = $presentation_props['publish_dir'] + publish_dir = @presentation_props['publish_dir'] BigBlueButton.logger.info('setting playback url info') playback_protocol = bbb_props['playback_protocol'] playback_host = bbb_props['playback_host'] BigBlueButton.logger.info('setting target dir') - target_dir = "#{recording_dir}/publish/presentation/#{$meeting_id}" - $deskshare_dir = "#{recording_dir}/raw/#{$meeting_id}/deskshare" + target_dir = "#{recording_dir}/publish/presentation/#{@meeting_id}" + @deskshare_dir = "#{recording_dir}/raw/#{@meeting_id}/deskshare" if !FileTest.directory?(target_dir) BigBlueButton.logger.info('Making dir target_dir') FileUtils.mkdir_p target_dir - package_dir = "#{target_dir}/#{$meeting_id}" + package_dir = "#{target_dir}/#{@meeting_id}" BigBlueButton.logger.info('Making dir package_dir') FileUtils.mkdir_p package_dir begin - video_formats = $presentation_props['video_formats'] + video_formats = @presentation_props['video_formats'] - video_files = Dir.glob("#{$process_dir}/webcams.{#{video_formats.join(',')}}") + video_files = Dir.glob("#{@process_dir}/webcams.{#{video_formats.join(',')}}") if !video_files.empty? BigBlueButton.logger.info('Making video dir') video_dir = "#{package_dir}/video" @@ -1259,23 +1259,23 @@ begin audio_dir = "#{package_dir}/audio" BigBlueButton.logger.info('Making audio dir') FileUtils.mkdir_p audio_dir - BigBlueButton.logger.info("Made audio dir - copying: #{$process_dir}/audio.webm to -> #{audio_dir}") - FileUtils.cp("#{$process_dir}/audio.webm", audio_dir) - BigBlueButton.logger.info("Copied audio.webm file - copying: #{$process_dir}/audio.ogg to -> #{audio_dir}") - FileUtils.cp("#{$process_dir}/audio.ogg", audio_dir) + BigBlueButton.logger.info("Made audio dir - copying: #{@process_dir}/audio.webm to -> #{audio_dir}") + FileUtils.cp("#{@process_dir}/audio.webm", audio_dir) + BigBlueButton.logger.info("Copied audio.webm file - copying: #{@process_dir}/audio.ogg to -> #{audio_dir}") + FileUtils.cp("#{@process_dir}/audio.ogg", audio_dir) BigBlueButton.logger.info('Copied audio.ogg file') end - if File.exist?("#{$process_dir}/captions.json") + if File.exist?("#{@process_dir}/captions.json") BigBlueButton.logger.info('Copying caption files') - FileUtils.cp("#{$process_dir}/captions.json", package_dir) - Dir.glob("#{$process_dir}/caption_*.vtt").each do |caption| + FileUtils.cp("#{@process_dir}/captions.json", package_dir) + Dir.glob("#{@process_dir}/caption_*.vtt").each do |caption| BigBlueButton.logger.debug(caption) FileUtils.cp(caption, package_dir) end end - video_files = Dir.glob("#{$process_dir}/deskshare.{#{video_formats.join(',')}}") + video_files = Dir.glob("#{@process_dir}/deskshare.{#{video_formats.join(',')}}") if !video_files.empty? BigBlueButton.logger.info('Making deskshare dir') deskshare_dir = "#{package_dir}/deskshare" @@ -1289,44 +1289,44 @@ begin BigBlueButton.logger.info("Could not copy deskshares.webm: file doesn't exist") end - if File.exist?("#{$process_dir}/presentation_text.json") - FileUtils.cp("#{$process_dir}/presentation_text.json", package_dir) + if File.exist?("#{@process_dir}/presentation_text.json") + FileUtils.cp("#{@process_dir}/presentation_text.json", package_dir) end - FileUtils.cp("#{$process_dir}/notes/notes.html", package_dir) if File.exist?("#{$process_dir}/notes/notes.html") + FileUtils.cp("#{@process_dir}/notes/notes.html", package_dir) if File.exist?("#{@process_dir}/notes/notes.html") - processing_time = File.read("#{$process_dir}/processing_time") + processing_time = File.read("#{@process_dir}/processing_time") - @doc = Nokogiri::XML(File.read("#{$process_dir}/events.xml")) + @doc = Nokogiri::XML(File.read("#{@process_dir}/events.xml")) # Retrieve record events and calculate total recording duration. - $rec_events = BigBlueButton::Events.match_start_and_stop_rec_events( + @rec_events = BigBlueButton::Events.match_start_and_stop_rec_events( BigBlueButton::Events.get_start_and_stop_rec_events(@doc) ) recording_time = BigBlueButton::Events.get_recording_length(@doc) - # presentation_url = "/slides/" + $meeting_id + "/presentation" + # presentation_url = "/slides/" + @meeting_id + "/presentation" - $meeting_start = @doc.xpath('//event')[0][:timestamp] - $meeting_end = @doc.xpath('//event').last[:timestamp] + @meeting_start = @doc.xpath('//event')[0][:timestamp] + @meeting_end = @doc.xpath('//event').last[:timestamp] - $version_atleast_0_9_0 = BigBlueButton::Events.bbb_version_compare( + @version_atleast_0_9_0 = BigBlueButton::Events.bbb_version_compare( @doc, 0, 9, 0 ) - $version_atleast_2_0_0 = BigBlueButton::Events.bbb_version_compare( + @version_atleast_2_0_0 = BigBlueButton::Events.bbb_version_compare( @doc, 2, 0, 0 ) BigBlueButton.logger.info('Creating metadata.xml') # Get the real-time start and end timestamp - # match = /.*-(\d+)$/.match($meeting_id) + # match = /.*-(\d+)@/.match(@meeting_id) # real_start_time = match[1] - # real_end_time = (real_start_time.to_i + ($meeting_end.to_i - $meeting_start.to_i)).to_s + # real_end_time = (real_start_time.to_i + (@meeting_end.to_i - @meeting_start.to_i)).to_s #### INSTEAD OF CREATING THE WHOLE metadata.xml FILE AGAIN, ONLY ADD # Copy metadata.xml from process_dir - FileUtils.cp("#{$process_dir}/metadata.xml", package_dir) + FileUtils.cp("#{@process_dir}/metadata.xml", package_dir) BigBlueButton.logger.info('Copied metadata.xml file') # Update state and add playback to metadata.xml @@ -1341,11 +1341,11 @@ begin ## Remove empty playback metadata.search('//recording/playback').each(&:remove) ## Add the actual playback - presentation = BigBlueButton::Presentation.get_presentation_for_preview($process_dir.to_s) + presentation = BigBlueButton::Presentation.get_presentation_for_preview(@process_dir.to_s) Nokogiri::XML::Builder.with(metadata.at('recording')) do |xml| xml.playback do xml.format('presentation') - xml.link("#{playback_protocol}://#{playback_host}/playback/presentation/2.3/#{$meeting_id}") + xml.link("#{playback_protocol}://#{playback_host}/playback/presentation/2.3/#{@meeting_id}") xml.processing_time(processing_time.to_s) xml.duration(recording_time.to_s) unless presentation.empty? @@ -1354,7 +1354,7 @@ begin xml.images do presentation[:slides].each do |key, val| attributes = { width: '176', height: '136', alt: !val[:alt].nil? ? (val[:alt]).to_s : '' } - xml.image(attributes) { xml.text("#{playback_protocol}://#{playback_host}/presentation/#{$meeting_id}/presentation/#{presentation[:id]}/thumbnails/thumb-#{key}.png") } + xml.image(attributes) { xml.text("#{playback_protocol}://#{playback_host}/presentation/#{@meeting_id}/presentation/#{presentation[:id]}/thumbnails/thumb-#{key}.png") } end end end @@ -1387,10 +1387,10 @@ begin processExternalVideoEvents(@doc, package_dir) # Write deskshare.xml to file - File.open("#{package_dir}/#{$deskshare_xml_filename}", 'w') { |f| f.puts $deskshare_xml.to_xml } + File.open("#{package_dir}/#{@deskshare_xml_filename}", 'w') { |f| f.puts @deskshare_xml.to_xml } BigBlueButton.logger.info('Copying files to package dir') - FileUtils.cp_r("#{$process_dir}/presentation", package_dir) + FileUtils.cp_r("#{@process_dir}/presentation", package_dir) BigBlueButton.logger.info('Copied files to package dir') BigBlueButton.logger.info('Publishing slides') @@ -1398,7 +1398,7 @@ begin FileUtils.mkdir_p publish_dir unless FileTest.directory?(publish_dir) # Get raw size of presentation files - raw_dir = "#{recording_dir}/raw/#{$meeting_id}" + raw_dir = "#{recording_dir}/raw/#{@meeting_id}" # After all the processing we'll add the published format and raw sizes to the metadata file BigBlueButton.add_raw_size_to_metadata(package_dir, raw_dir) BigBlueButton.add_playback_size_to_metadata(package_dir) @@ -1408,7 +1408,7 @@ begin # TODO: remove comments # BigBlueButton.logger.info("Removing processed files.") - # FileUtils.rm_r(Dir.glob("#{$process_dir}/*")) + # FileUtils.rm_r(Dir.glob("#{@process_dir}/*")) BigBlueButton.logger.info('Removing published files.') FileUtils.rm_r(Dir.glob("#{target_dir}/*")) @@ -1419,8 +1419,8 @@ begin end exit 1 end - publish_done = File.new("#{recording_dir}/status/published/#{$meeting_id}-presentation.done", 'w') - publish_done.write("Published #{$meeting_id}") + publish_done = File.new("#{recording_dir}/status/published/#{@meeting_id}-presentation.done", 'w') + publish_done.write("Published #{@meeting_id}") publish_done.close else @@ -1432,8 +1432,8 @@ rescue StandardError => e e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) end - publish_done = File.new("#{recording_dir}/status/published/#{$meeting_id}-presentation.fail", 'w') - publish_done.write("Failed Publishing #{$meeting_id}") + publish_done = File.new("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') + publish_done.write("Failed Publishing #{@meeting_id}") publish_done.close exit 1 From 8901f2a9361c4ea9ce31a8af925a2dbee41d1737 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sun, 5 Dec 2021 20:09:46 +0100 Subject: [PATCH 06/26] snake_case for methods --- .../scripts/publish/presentation.rb | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 9c1440a450..48948b17e8 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -44,7 +44,7 @@ bbb_props = BigBlueButton.read_props # by 2. This is just here to call out how spooky that is. @magic_mystery_number = 2 -def scaleToDeskshareVideo(width, height) +def scale_to_deskshare_video(width, height) deskshare_video_height = @presentation_props['deskshare_output_height'].to_f deskshare_video_width = @presentation_props['deskshare_output_height'].to_f @@ -55,14 +55,14 @@ def scaleToDeskshareVideo(width, height) [video_width.floor, video_height.floor] end -def getDeskshareVideoDimension(deskshare_stream_name) +def get_deskshare_video_dimension(deskshare_stream_name) video_width = @presentation_props['deskshare_output_height'].to_f video_height = @presentation_props['deskshare_output_height'].to_f deskshare_video_filename = "#{@deskshare_dir}/#{deskshare_stream_name}" if File.exist?(deskshare_video_filename) video_info = BigBlueButton::EDL::Video.video_info(deskshare_video_filename) - video_width, video_height = scaleToDeskshareVideo(video_info[:width], video_info[:height]) + video_width, video_height = scale_to_deskshare_video(video_info[:width], video_info[:height]) else BigBlueButton.logger.error("Could not find deskshare video: #{deskshare_video_filename}") end @@ -74,7 +74,7 @@ end # Calculate the offsets based on the start and stop recording events, so it's easier # to translate the timestamps later based on these offsets # -def calculateRecordEventsOffset +def calculate_record_events_offset accumulated_duration = 0 previous_stop_recording = @meeting_start.to_f @rec_events.each do |event| @@ -91,8 +91,8 @@ end # Translated an arbitrary Unix timestamp to the recording timestamp. This is the # function that others will call # -def translateTimestamp(timestamp) - new_timestamp = translateTimestamp_helper(timestamp.to_f).to_f +def translate_timestamp(timestamp) + new_timestamp = translate_timestamp_helper(timestamp.to_f).to_f # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-@meeting_start.to_f}, new value=#{new_timestamp}") new_timestamp end @@ -100,7 +100,7 @@ end # # Translated an arbitrary Unix timestamp to the recording timestamp # -def translateTimestamp_helper(timestamp) +def translate_timestamp_helper(timestamp) @rec_events.each do |event| # if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording return event[:start_timestamp] - event[:offset] if timestamp <= event[:start_timestamp] @@ -819,7 +819,7 @@ end # Create the shapes.svg, cursors.xml, and panzooms.xml files used for # rendering the presentation area -def processPresentation(package_dir) +def process_presentation(package_dir) shapes_doc = Nokogiri::XML::Document.new shapes_doc.create_internal_subset('svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd') @@ -868,7 +868,7 @@ def processPresentation(package_dir) events_xml.xpath('/recording/event').each do |event| eventname = event['eventname'] last_timestamp = timestamp = - (translateTimestamp(event['timestamp']) / 1000.0).round(1) + (translate_timestamp(event['timestamp']) / 1000.0).round(1) # Make sure to add initial entries to the slide & panzoom lists slide_changed = slides.empty? @@ -1045,7 +1045,7 @@ def processPresentation(package_dir) File.write("#{package_dir}/#{@cursor_xml_filename}", cursors_doc.to_xml) end -def processChatMessages(events, bbb_props) +def process_chat_messages(events, bbb_props) BigBlueButton.logger.info('Processing chat events') # Create slides.xml and chat. Nokogiri::XML::Builder.new do |xml| @@ -1065,7 +1065,7 @@ def processChatMessages(events, bbb_props) end end -def processDeskshareEvents(events) +def process_deskshare_events(events) BigBlueButton.logger.info('Processing deskshare events') deskshare_matched_events = BigBlueButton::Events.get_matched_start_and_stop_deskshare_events(events) @@ -1073,15 +1073,15 @@ def processDeskshareEvents(events) @xml = xml @xml.recording('id' => 'deskshare_events') do deskshare_matched_events.each do |event| - start_timestamp = (translateTimestamp(event[:start_timestamp].to_f) / 1000).round(1) - stop_timestamp = (translateTimestamp(event[:stop_timestamp].to_f) / 1000).round(1) + start_timestamp = (translate_timestamp(event[:start_timestamp].to_f) / 1000).round(1) + stop_timestamp = (translate_timestamp(event[:stop_timestamp].to_f) / 1000).round(1) next unless start_timestamp != stop_timestamp video_info = BigBlueButton::EDL::Video.video_info("#{@deskshare_dir}/#{event[:stream]}") unless video_info[:video] BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") next end - video_width, video_height = getDeskshareVideoDimension(event[:stream]) + video_width, video_height = get_deskshare_video_dimension(event[:stream]) @xml.event(start_timestamp: start_timestamp, stop_timestamp: stop_timestamp, video_width: video_width, @@ -1091,47 +1091,47 @@ def processDeskshareEvents(events) end end -def getPollQuestion(event) +def get_poll_question(event) question = '' question = event.at_xpath('question').text unless event.at_xpath('question').nil? question end -def getPollAnswers(event) +def get_poll_answers(event) answers = [] answers = JSON.parse(event.at_xpath('answers').content) unless event.at_xpath('answers').nil? answers end -def getPollRespondents(event) +def get_poll_respondents(event) respondents = 0 respondents = event.at_xpath('numRespondents').text.to_i unless event.at_xpath('numRespondents').nil? respondents end -def getPollResponders(event) +def get_poll_responders(event) responders = 0 responders = event.at_xpath('numResponders').text.to_i unless event.at_xpath('numResponders').nil? responders end -def getPollId(event) +def get_poll_id(event) id = '' id = event.at_xpath('pollId').text unless event.at_xpath('pollId').nil? id end -def getPollType(events, published_poll_event) - published_poll_id = getPollId(published_poll_event) +def get_poll_type(events, published_poll_event) + published_poll_id = get_poll_id(published_poll_event) type = '' events.xpath("//event[@eventname='PollStartedRecordEvent']").each do |event| - poll_id = getPollId(event) + poll_id = get_poll_id(event) if poll_id.eql?(published_poll_id) type = event.at_xpath('type').text @@ -1142,7 +1142,7 @@ def getPollType(events, published_poll_event) type end -def processPollEvents(events, package_dir) +def process_poll_events(events, package_dir) BigBlueButton.logger.info('Processing poll events') published_polls = [] @@ -1150,12 +1150,12 @@ def processPollEvents(events, package_dir) events.xpath("//event[@eventname='PollPublishedRecordEvent']").each do |event| next unless (event[:timestamp].to_i >= re[:start_timestamp]) && (event[:timestamp].to_i <= re[:stop_timestamp]) published_polls << { - timestamp: (translateTimestamp(event[:timestamp]) / 1000).to_i, - type: getPollType(events, event), - question: getPollQuestion(event), - answers: getPollAnswers(event), - respondents: getPollRespondents(event), - responders: getPollResponders(event) + timestamp: (translate_timestamp(event[:timestamp]) / 1000).to_i, + type: get_poll_type(events, event), + question: get_poll_question(event), + answers: get_poll_answers(event), + respondents: get_poll_respondents(event), + responders: get_poll_responders(event) } end end @@ -1163,7 +1163,7 @@ def processPollEvents(events, package_dir) File.open("#{package_dir}/polls.json", 'w') { |f| f.puts(published_polls.to_json) } unless published_polls.empty? end -def processExternalVideoEvents(_events, package_dir) +def process_external_video_events(_events, package_dir) BigBlueButton.logger.info('Processing external video events') # Retrieve external video events @@ -1175,7 +1175,7 @@ def processExternalVideoEvents(_events, package_dir) @rec_events.each do |re| external_video_events.each do |event| # BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}") - timestamp = (translateTimestamp(event[:start_timestamp]) / 1000).to_i + timestamp = (translate_timestamp(event[:start_timestamp]) / 1000).to_i # do not add same external_video twice next unless external_videos.find { |ev| ev[:timestamp] == timestamp }.nil? if ((event[:start_timestamp] >= re[:start_timestamp]) && (event[:start_timestamp] <= re[:stop_timestamp])) || @@ -1188,11 +1188,7 @@ def processExternalVideoEvents(_events, package_dir) end end - unless external_videos.empty? - File.open("#{package_dir}/external_videos.json", 'w') do |f| - f.puts(external_videos.to_json) - end - end + File.open("#{package_dir}/external_videos.json", 'w') { |f| f.puts(external_videos.to_json) } unless external_videos.empty? end @shapes_svg_filename = 'shapes.svg' @@ -1372,19 +1368,19 @@ begin # Create slides.xml BigBlueButton.logger.info('Generating xml for slides and chat') - calculateRecordEventsOffset + calculate_record_events_offset # Write slides.xml to file - slides_doc = processChatMessages(@doc, bbb_props) + slides_doc = process_chat_messages(@doc, bbb_props) File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts slides_doc.to_xml } - processPresentation(package_dir) + process_presentation(package_dir) - processDeskshareEvents(@doc) + process_deskshare_events(@doc) - processPollEvents(@doc, package_dir) + process_poll_events(@doc, package_dir) - processExternalVideoEvents(@doc, package_dir) + process_external_video_events(@doc, package_dir) # Write deskshare.xml to file File.open("#{package_dir}/#{@deskshare_xml_filename}", 'w') { |f| f.puts @deskshare_xml.to_xml } From e7661f385d9313ac6ec74fc1291d0542b1165134 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 6 Dec 2021 12:40:11 +0100 Subject: [PATCH 07/26] Refactor sections of identical code --- .../scripts/publish/presentation.rb | 131 ++++++++---------- 1 file changed, 60 insertions(+), 71 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 48948b17e8..3d4c57e6c5 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -227,9 +227,13 @@ def svg_render_shape_line(g, slide, shape) g << line end +def stroke_attributes(slide, shape) + "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" +end + def svg_render_shape_rect(g, slide, shape) g['shape'] = "rect#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" + g['style'] = stroke_attributes(slide, shape) g['style'] += if @version_atleast_2_0_0 ';stroke-linejoin:miter' else @@ -264,7 +268,7 @@ end def svg_render_shape_triangle(g, slide, shape) g['shape'] = "triangle#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" + g['style'] = stroke_attributes(slide, shape) g['style'] += if @version_atleast_2_0_0 ';stroke-linejoin:miter;stroke-miterlimit:8' else @@ -287,7 +291,7 @@ end def svg_render_shape_ellipse(g, slide, shape) g['shape'] = "ellipse#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" + g['style'] = stroke_attributes(slide, shape) doc = g.document data_points = shape[:data_points] @@ -553,22 +557,27 @@ end @svg_shape_id = 1 @svg_shape_unique_id = 1 + +def determine_presentation(presentation, current_presentation) + return current_presentation if presentation.nil? + presentation.text +end + +def determine_slide_number(slide, current_slide) + return current_slide if slide.nil? + slide = slide.text.to_i + slide -= 1 unless @version_atleast_0_9_0 + slide +end + def events_parse_shape(shapes, event, current_presentation, current_slide, timestamp) # Figure out what presentation+slide this shape is for, with fallbacks # for old BBB where this info isn't in the shape messages presentation = event.at_xpath('presentation') slide = event.at_xpath('pageNumber') - presentation = if presentation.nil? - current_presentation - else - presentation.text - end - if slide.nil? - slide = current_slide - else - slide = slide.text.to_i - slide -= 1 unless @version_atleast_0_9_0 - end + + presentation = determine_presentation(presentation, current_presentation) + slide = determine_slide_number(slide, current_slide) # Set up the shapes data structures if needed shapes[presentation] = {} if shapes[presentation].nil? @@ -594,9 +603,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times @svg_shape_id += 1 # Some shape-specific properties - if (shape[:type] == 'pencil') || (shape[:type] == 'rectangle') || - (shape[:type] == 'ellipse') || (shape[:type] == 'triangle') || - (shape[:type] == 'line') + if %w[ellipse line pencil rectangle triangle].include?(shape[:type]) shape[:color] = color_to_hex(event.at_xpath('color').text) thickness = event.at_xpath('thickness') unless thickness @@ -609,31 +616,28 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:thickness] = thickness.text.to_i end end - if (shape[:type] == 'rectangle') || - (shape[:type] == 'ellipse') || (shape[:type] == 'triangle') + if %w[ellipse rectangle triangle].include?(shape[:type]) # TODO: uncomment this # fill = event.at_xpath('fill').text # shape[:fill] = fill =~ /true/ ? true : false shape[:fill] = false end - if shape[:type] == 'rectangle' + + case shape[:type] + when 'rectangle' square = event.at_xpath('square') shape[:square] = (square.text == 'true') unless square.nil? - end - if shape[:type] == 'ellipse' + when 'ellipse' circle = event.at_xpath('circle') shape[:circle] = (circle.text == 'true') unless circle.nil? - end - if shape[:type] == 'pencil' + when 'pencil' commands = event.at_xpath('commands') shape[:commands] = commands.text.split(',').map(&:to_i) unless commands.nil? - end - if shape[:type] == 'poll_result' + when 'poll_result' shape[:num_responders] = event.at_xpath('num_responders').text.to_i shape[:num_respondents] = event.at_xpath('num_respondents').text.to_i shape[:result] = event.at_xpath('result').text - end - if shape[:type] == 'text' + when 'text' shape[:text_box_width] = event.at_xpath('textBoxWidth').text.to_f shape[:text_box_height] = event.at_xpath('textBoxHeight').text.to_f @@ -647,11 +651,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:font_color] = color_to_hex(event.at_xpath('fontColor').text) text = event.at_xpath('text') - shape[:text] = if !text.nil? - text.text - else - '' - end + shape[:text] = !text.nil? ? text.text : '' end # Find the previous shape, for updates @@ -692,17 +692,10 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # for old BBB where this info isn't in the undo messages presentation = event.at_xpath('presentation') slide = event.at_xpath('pageNumber') - presentation = if presentation.nil? - current_presentation - else - presentation.text - end - if slide.nil? - slide = current_slide - else - slide = slide.text.to_i - slide -= 1 unless @version_atleast_0_9_0 - end + + presentation = determine_presentation(presentation, current_presentation) + slide = determine_slide_number(slide, current_slide) + # Newer undo messages have the shape id, making this a lot easier shape_id = event.at_xpath('shapeId') shape_id = shape_id.text unless shape_id.nil? @@ -746,17 +739,9 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times # for old BBB where this info isn't in the clear messages presentation = event.at_xpath('presentation') slide = event.at_xpath('pageNumber') - presentation = if presentation.nil? - current_presentation - else - presentation.text - end - if slide.nil? - slide = current_slide - else - slide = slide.text.to_i - slide -= 1 unless @version_atleast_0_9_0 - end + + presentation = determine_presentation(presentation, current_presentation) + slide = determine_slide_number(slide, current_slide) # BigBlueButton 2.0 per-user clear features full_clear = event.at_xpath('fullClear') @@ -876,56 +861,61 @@ def process_presentation(package_dir) cursor_changed = cursors.empty? # Do event specific processing - if eventname == 'SharePresentationEvent' + case eventname + when 'SharePresentationEvent' current_presentation = event.at_xpath('presentationName').text current_slide = current_presentation_slide[current_presentation].to_i slide_changed = true panzoom_changed = true - elsif eventname == 'GotoSlideEvent' + when 'GotoSlideEvent' current_slide = event.at_xpath('slide').text.to_i current_presentation_slide[current_presentation] = current_slide slide_changed = true panzoom_changed = true - elsif eventname == 'ResizeAndMoveSlideEvent' + when 'ResizeAndMoveSlideEvent' current_x_offset = event.at_xpath('xOffset').text.to_f current_y_offset = event.at_xpath('yOffset').text.to_f current_width_ratio = event.at_xpath('widthRatio').text.to_f current_height_ratio = event.at_xpath('heightRatio').text.to_f panzoom_changed = true - elsif @presentation_props['include_deskshare'] && ((eventname == 'DeskshareStartedEvent') || (eventname == 'StartWebRTCDesktopShareEvent')) - deskshare = true - slide_changed = true + when 'DeskshareStartedEvent', 'StartWebRTCDesktopShareEvent' + if @presentation_props['include_deskshare'] + deskshare = true + slide_changed = true + end - elsif @presentation_props['include_deskshare'] && ((eventname == 'DeskshareStoppedEvent') || (eventname == 'StopWebRTCDesktopShareEvent')) - deskshare = false - slide_changed = true + when 'DeskshareStoppedEvent', 'StopWebRTCDesktopShareEvent' + if @presentation_props['include_deskshare'] + deskshare = false + slide_changed = true + end - elsif (eventname == 'AddShapeEvent') || (eventname == 'ModifyTextEvent') + when 'AddShapeEvent', 'ModifyTextEvent' events_parse_shape(shapes, event, current_presentation, current_slide, timestamp) - elsif (eventname == 'UndoShapeEvent') || (eventname == 'UndoAnnotationEvent') + when 'UndoShapeEvent', 'UndoAnnotationEvent' events_parse_undo(shapes, event, current_presentation, current_slide, timestamp) - elsif (eventname == 'ClearPageEvent') || (eventname == 'ClearWhiteboardEvent') + when 'ClearPageEvent', 'ClearWhiteboardEvent' events_parse_clear(shapes, event, current_presentation, current_slide, timestamp) - elsif eventname == 'AssignPresenterEvent' + when 'AssignPresenterEvent' # Move cursor offscreen on presenter switch, it'll reappear if the new # presenter moves it presenter = event.at_xpath('userid').text cursor_visible = false cursor_changed = true - elsif eventname == 'CursorMoveEvent' + when 'CursorMoveEvent' cursor_x = event.at_xpath('xOffset').text.to_f cursor_y = event.at_xpath('yOffset').text.to_f cursor_visible = true cursor_changed = true - elsif eventname == 'WhiteboardCursorMoveEvent' + when 'WhiteboardCursorMoveEvent' user_id = event.at_xpath('userId') # Only draw cursor for current presentor. TODO multi-cursor support if user_id.nil? || (user_id.text == presenter) @@ -935,7 +925,6 @@ def process_presentation(package_dir) cursor_changed = true end end - # Perform slide finalization if slide_changed slide = slides.last From b3601eb8df3e883c2b742a8e6be9741a8fe2de54 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 6 Dec 2021 13:58:04 +0100 Subject: [PATCH 08/26] Performance: << faster than += for strings --- .../scripts/publish/presentation.rb | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 3d4c57e6c5..e9b41dbaaf 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: true +# frozen_string_literal: false # Set encoding to utf-8 # encoding: UTF-8 @@ -210,12 +210,7 @@ end def svg_render_shape_line(g, slide, shape) g['shape'] = "line#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none" - g['style'] += if @version_atleast_2_0_0 - ';stroke-linecap:butt' - else - ';stroke-linecap:round' - end + g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none#{@version_atleast_2_0_0 ? ";stroke-linecap:butt" : ";stroke-linecap:round"}" doc = g.document data_points = shape[:data_points] @@ -233,12 +228,7 @@ end def svg_render_shape_rect(g, slide, shape) g['shape'] = "rect#{shape[:shape_unique_id]}" - g['style'] = stroke_attributes(slide, shape) - g['style'] += if @version_atleast_2_0_0 - ';stroke-linejoin:miter' - else - ';stroke-linejoin:round' - end + g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ";stroke-linejoin:miter" : ";stroke-linejoin:round"}" doc = g.document data_points = shape[:data_points] @@ -268,12 +258,7 @@ end def svg_render_shape_triangle(g, slide, shape) g['shape'] = "triangle#{shape[:shape_unique_id]}" - g['style'] = stroke_attributes(slide, shape) - g['style'] += if @version_atleast_2_0_0 - ';stroke-linejoin:miter;stroke-miterlimit:8' - else - ';stroke-linejoin:round' - end + g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ";stroke-linejoin:miter;stroke-miterlimit:8" : ";stroke-linejoin:round"}" doc = g.document data_points = shape[:data_points] @@ -326,11 +311,11 @@ def svg_render_shape_ellipse(g, slide, shape) # path element's elliptical arc code renders r_x or r_y # degenerate cases as line segments, so we can use that. path = "M#{x1} #{hy}" - path += "A#{width_r} #{height_r} 0 0 1 #{hx} #{y1}" - path += "A#{width_r} #{height_r} 0 0 1 #{x2} #{hy}" - path += "A#{width_r} #{height_r} 0 0 1 #{hx} #{y2}" - path += "A#{width_r} #{height_r} 0 0 1 #{x1} #{hy}" - path += 'Z' + path << "A#{width_r} #{height_r} 0 0 1 #{hx} #{y1}" + path << "A#{width_r} #{height_r} 0 0 1 #{x2} #{hy}" + path << "A#{width_r} #{height_r} 0 0 1 #{hx} #{y2}" + path << "A#{width_r} #{height_r} 0 0 1 #{x1} #{hy}" + path << 'Z' svg_path = doc.create_element('path', d: path) g << svg_path From 3dcacea3793a9d13482e2b5988488879a8bcc5dc Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 6 Dec 2021 14:22:06 +0100 Subject: [PATCH 09/26] Optimize XPATH queries --- .../presentation/scripts/publish/presentation.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index e9b41dbaaf..ccfcedb817 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -210,7 +210,7 @@ end def svg_render_shape_line(g, slide, shape) g['shape'] = "line#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none#{@version_atleast_2_0_0 ? ";stroke-linecap:butt" : ";stroke-linecap:round"}" + g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none#{@version_atleast_2_0_0 ? ';stroke-linecap:butt' : ';stroke-linecap:round'}" doc = g.document data_points = shape[:data_points] @@ -228,7 +228,7 @@ end def svg_render_shape_rect(g, slide, shape) g['shape'] = "rect#{shape[:shape_unique_id]}" - g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ";stroke-linejoin:miter" : ";stroke-linejoin:round"}" + g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter' : ';stroke-linejoin:round'}" doc = g.document data_points = shape[:data_points] @@ -258,7 +258,7 @@ end def svg_render_shape_triangle(g, slide, shape) g['shape'] = "triangle#{shape[:shape_unique_id]}" - g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ";stroke-linejoin:miter;stroke-miterlimit:8" : ";stroke-linejoin:round"}" + g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter;stroke-miterlimit:8' : ';stroke-linejoin:round'}" doc = g.document data_points = shape[:data_points] @@ -1104,7 +1104,7 @@ def get_poll_type(events, published_poll_event) published_poll_id = get_poll_id(published_poll_event) type = '' - events.xpath("//event[@eventname='PollStartedRecordEvent']").each do |event| + events.xpath("recording/event[@eventname='PollStartedRecordEvent']").each do |event| poll_id = get_poll_id(event) if poll_id.eql?(published_poll_id) @@ -1121,7 +1121,7 @@ def process_poll_events(events, package_dir) published_polls = [] @rec_events.each do |re| - events.xpath("//event[@eventname='PollPublishedRecordEvent']").each do |event| + events.xpath("recording/event[@eventname='PollPublishedRecordEvent']").each do |event| next unless (event[:timestamp].to_i >= re[:start_timestamp]) && (event[:timestamp].to_i <= re[:stop_timestamp]) published_polls << { timestamp: (translate_timestamp(event[:timestamp]) / 1000).to_i, @@ -1278,8 +1278,8 @@ begin # presentation_url = "/slides/" + @meeting_id + "/presentation" - @meeting_start = @doc.xpath('//event')[0][:timestamp] - @meeting_end = @doc.xpath('//event').last[:timestamp] + @meeting_start = BigBlueButton::Events.first_event_timestamp(@doc) + @meeting_end = BigBlueButton::Events.last_event_timestamp(@doc) @version_atleast_0_9_0 = BigBlueButton::Events.bbb_version_compare( @doc, 0, 9, 0 @@ -1309,7 +1309,7 @@ begin published = recording.at_xpath('published') published.content = 'true' ## Remove empty playback - metadata.search('//recording/playback').each(&:remove) + metadata.search('recording/playback').each(&:remove) ## Add the actual playback presentation = BigBlueButton::Presentation.get_presentation_for_preview(@process_dir.to_s) Nokogiri::XML::Builder.with(metadata.at('recording')) do |xml| From 5aff9c0923fb430ca49e6534dd04b48a2afdef1e Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 6 Dec 2021 18:59:10 +0100 Subject: [PATCH 10/26] Fasterer: quicker undo, prev_shape --- .../scripts/publish/presentation.rb | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index ccfcedb817..cfa7b5873f 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -643,7 +643,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times prev_shape = nil if !shape[:id].nil? # If we have a shape ID, look up the previous shape by ID - prev_shape = shapes.find_all { |s| s[:id] == shape[:id] }.last + prev_shape_pos = shapes.rindex { |s| s[:id] == shape[:id] } + prev_shape = prev_shape_pos.nil? ? nil : shapes[prev_shape_pos] else # No shape ID, so do heuristic matching. If the previous shape had the # same type and same first two data points, update it. @@ -668,7 +669,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times @svg_shape_unique_id += 1 end - BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} Type #{shape[:type]}") + # BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} Type #{shape[:type]}") shapes << shape end @@ -703,7 +704,8 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest else # The undo command removes the most recently added shape that has not # already been removed by another undo or clear. Find that shape. - undo_shape = shapes.select { |s| s[:undo].nil? }.last + undo_pos = shapes.rindex { |s| s[:undo].nil? } + undo_shape = undo_pos.nil? ? nil : shapes[undo_pos] if !undo_shape.nil? BigBlueButton.logger.info("Undo: removing Shape #{undo_shape[:shape_unique_id]} at #{timestamp}") # We have an id number assigned to associate all the updated versions @@ -728,14 +730,9 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times presentation = determine_presentation(presentation, current_presentation) slide = determine_slide_number(slide, current_slide) - # BigBlueButton 2.0 per-user clear features + # BigBlueButton 2.0 per-user clear features; default to full clear on older versions full_clear = event.at_xpath('fullClear') - full_clear = if !full_clear.nil? - (full_clear.text == 'true') - else - # Default to full clear on older versions - true - end + full_clear = !full_clear.nil? ? (full_clear.text == 'true') : true user_id = event.at_xpath('userId') user_id = user_id.text unless user_id.nil? @@ -746,11 +743,7 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times # We only need to deal with shapes for this slide shapes = shapes[presentation][slide] - if full_clear - BigBlueButton.logger.info('Clear: removing all shapes') - else - BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") - end + full_clear ? BigBlueButton.logger.info('Clear: removing all shapes') : BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") shapes.each do |shape| if full_clear || (user_id == shape[:user_id]) From 6b0f2e887756f8ed230eb8b61b6322c94fb3d0e4 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 6 Dec 2021 19:19:52 +0100 Subject: [PATCH 11/26] Don't log draw/cursor events as often + Shorter syntax for opening and closing files --- .../scripts/publish/presentation.rb | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index cfa7b5873f..f488c730ed 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -145,7 +145,7 @@ def svg_render_shape_pencil(g, slide, shape) end if shape[:data_points].length == 2 - BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing single point") + # BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing single point") g['style'] = "stroke:none;fill:##{shape[:color]};visibility:hidden" circle = doc.create_element('circle', cx: shape_scale_width(slide, shape[:data_points][0]), @@ -157,7 +157,7 @@ def svg_render_shape_pencil(g, slide, shape) data_points = shape[:data_points].each if !shape[:commands].nil? - BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing from command string (#{shape[:commands].length} commands)") + # BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing from command string (#{shape[:commands].length} commands)") shape[:commands].each do |command| case command when 1 # MOVE_TO @@ -187,7 +187,7 @@ def svg_render_shape_pencil(g, slide, shape) end end else - BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing simple line (#{shape[:data_points].length / 2} points)") + # BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing simple line (#{shape[:data_points].length / 2} points)") x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) path << "M#{x} #{y}" @@ -332,7 +332,7 @@ def svg_render_shape_text(g, slide, shape) height = shape_scale_height(slide, shape[:text_box_height]) font_size = shape_scale_height(slide, shape[:calced_font_size]) - BigBlueButton.logger.info("Text #{shape[:shape_unique_id]} width #{width} height #{height} font size #{font_size}") + # BigBlueButton.logger.info("Text #{shape[:shape_unique_id]} width #{width} height #{height} font size #{font_size}") g['style'] = "color:##{shape[:font_color]};word-wrap:break-word;visibility:hidden;font-family:Arial;font-size:#{font_size}px" switch = doc.create_element('switch') @@ -392,7 +392,7 @@ def svg_render_shape(canvas, slide, shape, image_id) return end - BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} Type #{shape[:type]} from #{shape[:in]} to #{shape[:out]} undo #{shape[:undo]}") + # BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} Type #{shape[:type]} from #{shape[:in]} to #{shape[:out]} undo #{shape[:undo]}") doc = canvas.document g = doc.create_element('g', @@ -501,7 +501,7 @@ end def cursors_emit_event(rec, cursor) if cursor[:in] == cursor[:out] - BigBlueButton.logger.info('Cursor: not emitting, duration rounds to 0') + # BigBlueButton.logger.info('Cursor: not emitting, duration rounds to 0') return end @@ -534,7 +534,7 @@ def cursors_emit_event(rec, cursor) cursor_e = doc.create_element('cursor') cursor_e.content = "#{x} #{y}" - BigBlueButton.logger.info("Cursor #{cursor_e.content} at #{cursor[:in]}") + # BigBlueButton.logger.info("Cursor #{cursor_e.content} at #{cursor[:in]}") event << cursor_e rec << event @@ -983,7 +983,7 @@ def process_presentation(package_dir) cursor[:out] = timestamp cursors_emit_event(cursors_rec, cursor) end - BigBlueButton.logger.info("Cursor: visible #{cursor_visible}, #{cursor_x} #{cursor_y} (#{panzoom[:width]}x#{panzoom[:height]})") + # BigBlueButton.logger.info("Cursor: visible #{cursor_visible}, #{cursor_x} #{cursor_y} (#{panzoom[:width]}x#{panzoom[:height]})") cursor = { visible: cursor_visible, x: cursor_x, @@ -1188,12 +1188,12 @@ begin recording_dir = bbb_props['recording_dir'] BigBlueButton.logger.info('Setting process dir') @process_dir = "#{recording_dir}/process/presentation/#{@meeting_id}" - BigBlueButton.logger.info('setting publish dir') + BigBlueButton.logger.info('Setting publish dir') publish_dir = @presentation_props['publish_dir'] - BigBlueButton.logger.info('setting playback url info') + BigBlueButton.logger.info('Setting playback url info') playback_protocol = bbb_props['playback_protocol'] playback_host = bbb_props['playback_host'] - BigBlueButton.logger.info('setting target dir') + BigBlueButton.logger.info('Setting target dir') target_dir = "#{recording_dir}/publish/presentation/#{@meeting_id}" @deskshare_dir = "#{recording_dir}/raw/#{@meeting_id}/deskshare" @@ -1326,10 +1326,7 @@ begin end end ## Write the new metadata.xml - metadata_file = File.new("#{package_dir}/metadata.xml", 'w') - metadata = Nokogiri::XML(metadata.to_xml, &:noblanks) - metadata_file.write(metadata.root) - metadata_file.close + File.open("#{package_dir}/metadata.xml", 'w') { |file| file.write(Nokogiri::XML(metadata.to_xml, &:noblanks).root) } BigBlueButton.logger.info('Added playback to metadata.xml') # Create slides.xml @@ -1382,9 +1379,7 @@ begin end exit 1 end - publish_done = File.new("#{recording_dir}/status/published/#{@meeting_id}-presentation.done", 'w') - publish_done.write("Published #{@meeting_id}") - publish_done.close + File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.done", 'w') { |file| file.write("Published #{@meeting_id}") } else BigBlueButton.logger.info("#{target_dir} is already there") @@ -1395,9 +1390,7 @@ rescue StandardError => e e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) end - publish_done = File.new("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') - publish_done.write("Failed Publishing #{@meeting_id}") - publish_done.close + File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') { |file| file.write("Failed Publishing #{@meeting_id}") } exit 1 end From b40bca23e8cd9b83733b317f2cd9a6ee89cce0ab Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Thu, 16 Dec 2021 18:29:35 +0100 Subject: [PATCH 12/26] cursor.xml, deskshare.xml, panzooms.xml using XML Builder --- .../scripts/publish/presentation.rb | 185 +++++++++--------- 1 file changed, 88 insertions(+), 97 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 88ff11c3ad..d3bef35237 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -485,18 +485,11 @@ def panzooms_emit_event(rec, panzoom) return end - doc = rec.document - event = doc.create_element('event', timestamp: panzoom[:in]) - - x, y, w, h = panzoom_viewbox(panzoom) - - viewbox = doc.create_element('viewBox') - viewbox.content = "#{x} #{y} #{w} #{h}" - - BigBlueButton.logger.info("Panzoom viewbox #{viewbox.content} at #{panzoom[:in]}") - - event << viewbox - rec << event + rec.event(timestamp: panzoom[:in]) { + x, y, w, h = panzoom_viewbox(panzoom) + rec.viewBox("#{x} #{y} #{w} #{h}") + # BigBlueButton.logger.info("Panzoom viewbox #{x} #{y} #{w} #{h}" at #{panzoom[:in]}") + } end def cursors_emit_event(rec, cursor) @@ -505,39 +498,34 @@ def cursors_emit_event(rec, cursor) return end - doc = rec.document - event = doc.create_element('event', timestamp: cursor[:in]) - - panzoom = cursor[:panzoom] - if cursor[:visible] - if @version_atleast_2_0_0 - # In BBB 2.0, the cursor now uses the same coordinate system as annotations - # Use the panzoom information to convert it to be relative to viewbox - x = (((cursor[:x] / 100.0) + (panzoom[:x_offset] * @magic_mystery_number / 100.0)) / - (panzoom[:width_ratio] / 100.0)).round(5) - y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * @magic_mystery_number / 100.0)) / - (panzoom[:height_ratio] / 100.0)).round(5) - if x.negative? || (x > 1) || y.negative? || (y > 1) - x = -1.0 - y = -1.0 + rec.event(timestamp: cursor[:in]){ + panzoom = cursor[:panzoom] + if cursor[:visible] + if @version_atleast_2_0_0 + # In BBB 2.0, the cursor now uses the same coordinate system as annotations + # Use the panzoom information to convert it to be relative to viewbox + x = (((cursor[:x] / 100.0) + (panzoom[:x_offset] * @magic_mystery_number / 100.0)) / + (panzoom[:width_ratio] / 100.0)).round(5) + y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * @magic_mystery_number / 100.0)) / + (panzoom[:height_ratio] / 100.0)).round(5) + if x.negative? || (x > 1) || y.negative? || (y > 1) + x = -1.0 + y = -1.0 + end + else + # Cursor position is relative to the visible area + x = cursor[:x].round(5) + y = cursor[:y].round(5) end else - # Cursor position is relative to the visible area - x = cursor[:x].round(5) - y = cursor[:y].round(5) + x = -1.0 + y = -1.0 end - else - x = -1.0 - y = -1.0 - end - - cursor_e = doc.create_element('cursor') - cursor_e.content = "#{x} #{y}" + + rec.cursor("#{x} #{y}") + } # BigBlueButton.logger.info("Cursor #{cursor_e.content} at #{cursor[:in]}") - - event << cursor_e - rec << event end @svg_shape_id = 1 @@ -793,13 +781,8 @@ def process_presentation(package_dir) version: '1.1', viewBox: '0 0 800 600') - panzooms_doc = Nokogiri::XML::Document.new - panzooms_rec = panzooms_doc.root = panzooms_doc.create_element('recording', - id: 'panzoom_events') - - cursors_doc = Nokogiri::XML::Document.new - cursors_rec = cursors_doc.root = cursors_doc.create_element('recording', - id: 'cursor_events') + panzooms_rec = Builder::XmlMarkup.new(:indent => 2, margin: 1) + cursors_rec = Builder::XmlMarkup.new(:indent => 2, margin: 1) # Current presentation/slide state current_presentation_slide = {} @@ -1005,56 +988,66 @@ def process_presentation(package_dir) cursor[:out] = last_timestamp cursors_emit_event(cursors_rec, cursor) + cursors_doc = Builder::XmlMarkup.new(indent: 2) + cursors_doc.instruct! + cursors_doc.recording(id: 'cursor_events') { |xml| xml << cursors_rec.target! } + + panzooms_doc = Builder::XmlMarkup.new(indent: 2) + panzooms_doc.instruct! + panzooms_doc.recording(id: 'panzoom_events') { |xml| xml << panzooms_rec.target! } + # And save the result File.write("#{package_dir}/#{@shapes_svg_filename}", shapes_doc.to_xml) - File.write("#{package_dir}/#{@panzooms_xml_filename}", panzooms_doc.to_xml) - File.write("#{package_dir}/#{@cursor_xml_filename}", cursors_doc.to_xml) + File.write("#{package_dir}/#{@panzooms_xml_filename}", panzooms_doc.target!) + File.write("#{package_dir}/#{@cursor_xml_filename}", cursors_doc.target!) end def process_chat_messages(events, bbb_props) BigBlueButton.logger.info('Processing chat events') # Create slides.xml and chat. - Nokogiri::XML::Builder.new do |xml| - xml.popcorn do - BigBlueButton::Events.get_chat_events(events, @meeting_start.to_i, @meeting_end.to_i, bbb_props).each do |chat| - chattimeline = { - in: (chat[:in] / 1000.0).round(1), - direction: 'down', - name: chat[:sender], - message: chat[:message], - target: 'chat' - } - chattimeline[:out] = (chat[:out] / 1000.0).round(1) unless chat[:out].nil? - xml.chattimeline(**chattimeline) - end + xml = Builder::XmlMarkup.new(:indent => 2) + xml.instruct! + xml.popcorn do + BigBlueButton::Events.get_chat_events(events, @meeting_start.to_i, @meeting_end.to_i, bbb_props).each do |chat| + chattimeline = { + in: (chat[:in] / 1000.0).round(1), + direction: 'down', + name: chat[:sender], + message: chat[:message], + target: 'chat' + } + chattimeline[:out] = (chat[:out] / 1000.0).round(1) unless chat[:out].nil? + xml.chattimeline(**chattimeline) end end + + xml end def process_deskshare_events(events) BigBlueButton.logger.info('Processing deskshare events') deskshare_matched_events = BigBlueButton::Events.get_matched_start_and_stop_deskshare_events(events) - @deskshare_xml = Nokogiri::XML::Builder.new do |xml| - @xml = xml - @xml.recording('id' => 'deskshare_events') do - deskshare_matched_events.each do |event| - start_timestamp = (translate_timestamp(event[:start_timestamp].to_f) / 1000).round(1) - stop_timestamp = (translate_timestamp(event[:stop_timestamp].to_f) / 1000).round(1) - next unless start_timestamp != stop_timestamp - video_info = BigBlueButton::EDL::Video.video_info("#{@deskshare_dir}/#{event[:stream]}") - unless video_info[:video] - BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") - next - end - video_width, video_height = get_deskshare_video_dimension(event[:stream]) - @xml.event(start_timestamp: start_timestamp, - stop_timestamp: stop_timestamp, - video_width: video_width, - video_height: video_height) + @deskshare_xml = Builder::XmlMarkup.new(:indent => 2) + @deskshare_xml.instruct! + + @deskshare_xml.recording('id' => 'deskshare_events') { + deskshare_matched_events.each do |event| + start_timestamp = (translate_timestamp(event[:start_timestamp].to_f) / 1000).round(1) + stop_timestamp = (translate_timestamp(event[:stop_timestamp].to_f) / 1000).round(1) + next unless start_timestamp != stop_timestamp + video_info = BigBlueButton::EDL::Video.video_info("#{@deskshare_dir}/#{event[:stream]}") + unless video_info[:video] + BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") + next end + video_width, video_height = get_deskshare_video_dimension(event[:stream]) + @deskshare_xml.event(start_timestamp: start_timestamp, + stop_timestamp: stop_timestamp, + video_width: video_width, + video_height: video_height) end - end + } end def get_poll_question(event) @@ -1177,9 +1170,7 @@ puts @playback begin if @playback == 'presentation' - log_dir = bbb_props['log_dir'] - logger = Logger.new("#{log_dir}/presentation/publish-#{@meeting_id}.log", 'daily') BigBlueButton.logger = logger @@ -1335,7 +1326,7 @@ begin # Write slides.xml to file slides_doc = process_chat_messages(@doc, bbb_props) - File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts slides_doc.to_xml } + File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts slides_doc.target! } process_presentation(package_dir) @@ -1346,7 +1337,7 @@ begin process_external_video_events(@doc, package_dir) # Write deskshare.xml to file - File.open("#{package_dir}/#{@deskshare_xml_filename}", 'w') { |f| f.puts @deskshare_xml.to_xml } + File.open("#{package_dir}/#{@deskshare_xml_filename}", 'w') { |f| f.puts @deskshare_xml.target! } BigBlueButton.logger.info('Copying files to package dir') FileUtils.cp_r("#{@process_dir}/presentation", package_dir) @@ -1371,12 +1362,12 @@ begin BigBlueButton.logger.info('Removing published files.') FileUtils.rm_r(Dir.glob("#{target_dir}/*")) - rescue StandardError => e - BigBlueButton.logger.error(e.message) - e.backtrace.each do |traceline| - BigBlueButton.logger.error(traceline) - end - exit 1 + #rescue StandardError => e + #BigBlueButton.logger.error(e.message) + #e.backtrace.each do |traceline| + #BigBlueButton.logger.error(traceline) + #end + #exit 1 end File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.done", 'w') { |file| file.write("Published #{@meeting_id}") } @@ -1384,12 +1375,12 @@ begin BigBlueButton.logger.info("#{target_dir} is already there") end end -rescue StandardError => e - BigBlueButton.logger.error(e.message) - e.backtrace.each do |traceline| - BigBlueButton.logger.error(traceline) - end - File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') { |file| file.write("Failed Publishing #{@meeting_id}") } +#rescue StandardError => e + #BigBlueButton.logger.error(e.message) + #e.backtrace.each do |traceline| + # BigBlueButton.logger.error(traceline) + #end + #File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') { |file| file.write("Failed Publishing #{@meeting_id}") } - exit 1 + #exit 1 end From c1740daa14286ffcafd3b43abe28fea8f7394659 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sat, 18 Dec 2021 19:04:20 +0100 Subject: [PATCH 13/26] Don't look for updates if drawing has already ended --- .../presentation/scripts/publish/presentation.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index d3bef35237..3cb7731ab5 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -630,8 +630,11 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times prev_shape = nil if !shape[:id].nil? # If we have a shape ID, look up the previous shape by ID - prev_shape_pos = shapes.rindex { |s| s[:id] == shape[:id] } - prev_shape = prev_shape_pos.nil? ? nil : shapes[prev_shape_pos] + # Don't look for updates if the drawing has ended + unless shape[:status] == 'DRAW_END' + prev_shape_pos = shapes.rindex { |s| s[:id] == shape[:id] } + prev_shape = prev_shape_pos.nil? ? nil : shapes[prev_shape_pos] + end else # No shape ID, so do heuristic matching. If the previous shape had the # same type and same first two data points, update it. From 0a2c49543e996e5b471c033891944609db8b94f2 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sat, 18 Dec 2021 20:05:47 +0100 Subject: [PATCH 14/26] Linting changes --- .../scripts/publish/presentation.rb | 161 ++++++++++-------- 1 file changed, 93 insertions(+), 68 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 3cb7731ab5..90fcdd3d0a 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -1,6 +1,5 @@ -# frozen_string_literal: false # Set encoding to utf-8 -# encoding: UTF-8 +# frozen_string_literal: false # # BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ @@ -24,7 +23,7 @@ _performance_start = Time.now # For DEVELOPMENT # Allows us to run the script manually -require File.expand_path('../../../../core/lib/recordandplayback', __FILE__) +require File.expand_path('../../../core/lib/recordandplayback', __dir__) # For PRODUCTION # require File.expand_path('../../../lib/recordandplayback', __FILE__) @@ -93,7 +92,7 @@ end # def translate_timestamp(timestamp) new_timestamp = translate_timestamp_helper(timestamp.to_f).to_f - # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-@meeting_start.to_f}, new value=#{new_timestamp}") + BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f - @meeting_start.to_f}, new value=#{new_timestamp}") new_timestamp end @@ -202,7 +201,9 @@ def svg_render_shape_pencil(g, slide, shape) end path = path.join('') - g['style'] = "stroke:##{shape[:color]};stroke-linecap:round;stroke-linejoin:round;stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none" + g['style'] = + "stroke:##{shape[:color]};stroke-linecap:round;stroke-linejoin:round;stroke-width:#{shape_thickness(slide, + shape)};visibility:hidden;fill:none" svg_path = doc.create_element('path', d: path) g << svg_path end @@ -210,7 +211,9 @@ end def svg_render_shape_line(g, slide, shape) g['shape'] = "line#{shape[:shape_unique_id]}" - g['style'] = "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none#{@version_atleast_2_0_0 ? ';stroke-linecap:butt' : ';stroke-linecap:round'}" + g['style'] = + "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, + shape)};visibility:hidden;fill:none#{@version_atleast_2_0_0 ? ';stroke-linecap:butt' : ';stroke-linecap:round'}" doc = g.document data_points = shape[:data_points] @@ -223,12 +226,14 @@ def svg_render_shape_line(g, slide, shape) end def stroke_attributes(slide, shape) - "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:#{shape[:fill] ? '#' + shape[:color] : 'none'}" + "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, + shape)};visibility:hidden;fill:#{shape[:fill] ? "##{shape[:color]}" : 'none'}" end def svg_render_shape_rect(g, slide, shape) g['shape'] = "rect#{shape[:shape_unique_id]}" - g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter' : ';stroke-linejoin:round'}" + g['style'] = + "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter' : ';stroke-linejoin:round'}" doc = g.document data_points = shape[:data_points] @@ -258,7 +263,9 @@ end def svg_render_shape_triangle(g, slide, shape) g['shape'] = "triangle#{shape[:shape_unique_id]}" - g['style'] = "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter;stroke-miterlimit:8' : ';stroke-linejoin:round'}" + g['style'] = + "#{stroke_attributes(slide, + shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter;stroke-miterlimit:8' : ';stroke-linejoin:round'}" doc = g.document data_points = shape[:data_points] @@ -310,12 +317,12 @@ def svg_render_shape_ellipse(g, slide, shape) # we want to display a line segment in that case. But the SVG # path element's elliptical arc code renders r_x or r_y # degenerate cases as line segments, so we can use that. - path = "M#{x1} #{hy}" - path << "A#{width_r} #{height_r} 0 0 1 #{hx} #{y1}" - path << "A#{width_r} #{height_r} 0 0 1 #{x2} #{hy}" - path << "A#{width_r} #{height_r} 0 0 1 #{hx} #{y2}" - path << "A#{width_r} #{height_r} 0 0 1 #{x1} #{hy}" - path << 'Z' + path = "M#{x1} #{hy}" \ + "A#{width_r} #{height_r} 0 0 1 #{hx} #{y1}" \ + "A#{width_r} #{height_r} 0 0 1 #{x2} #{hy}" \ + "A#{width_r} #{height_r} 0 0 1 #{hx} #{y2}" \ + "A#{width_r} #{height_r} 0 0 1 #{x1} #{hy}" \ + 'Z' svg_path = doc.create_element('path', d: path) g << svg_path @@ -371,7 +378,8 @@ def svg_render_shape_poll(g, slide, shape) # Save the poll json to a temp file IO.write(json_file, result) # Render the poll svg - ret = BigBlueButton.exec_ret('utils/gen_poll_svg', '-i', json_file, '-w', width.round.to_s, '-h', height.round.to_s, '-n', num_responders.to_s, '-o', svg_file) + ret = BigBlueButton.exec_ret('utils/gen_poll_svg', '-i', json_file, '-w', width.round.to_s, '-h', height.round.to_s, '-n', + num_responders.to_s, '-o', svg_file) raise 'Failed to generate poll svg' if ret != 0 # Poll image @@ -450,6 +458,7 @@ def svg_render_image(svg, slide, shapes) shapes[slide[:presentation]][slide[:slide]].nil? return end + shapes = shapes[slide[:presentation]][slide[:slide]] canvas = doc.create_element('g', @@ -485,11 +494,11 @@ def panzooms_emit_event(rec, panzoom) return end - rec.event(timestamp: panzoom[:in]) { + rec.event(timestamp: panzoom[:in]) do x, y, w, h = panzoom_viewbox(panzoom) rec.viewBox("#{x} #{y} #{w} #{h}") # BigBlueButton.logger.info("Panzoom viewbox #{x} #{y} #{w} #{h}" at #{panzoom[:in]}") - } + end end def cursors_emit_event(rec, cursor) @@ -498,7 +507,7 @@ def cursors_emit_event(rec, cursor) return end - rec.event(timestamp: cursor[:in]){ + rec.event(timestamp: cursor[:in]) do panzoom = cursor[:panzoom] if cursor[:visible] if @version_atleast_2_0_0 @@ -521,9 +530,9 @@ def cursors_emit_event(rec, cursor) x = -1.0 y = -1.0 end - + rec.cursor("#{x} #{y}") - } + end # BigBlueButton.logger.info("Cursor #{cursor_e.content} at #{cursor[:in]}") end @@ -533,11 +542,13 @@ end def determine_presentation(presentation, current_presentation) return current_presentation if presentation.nil? + presentation.text end def determine_slide_number(slide, current_slide) return current_slide if slide.nil? + slide = slide.text.to_i slide -= 1 unless @version_atleast_0_9_0 slide @@ -565,6 +576,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:in] = timestamp shape[:type] = event.at_xpath('type').text shape[:data_points] = event.at_xpath('dataPoints').text.split(',').map(&:to_f) + # These can be missing in old BBB versions, there are fallbacks user_id = event.at_xpath('userId') shape[:user_id] = user_id.text unless user_id.nil? @@ -591,7 +603,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times end if %w[ellipse rectangle triangle].include?(shape[:type]) fill = event.at_xpath('fill') - fill = fill.nil? ? "false" : fill.text + fill = fill.nil? ? 'false' : fill.text shape[:fill] = fill =~ /true/ ? true : false end @@ -689,6 +701,7 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest BigBlueButton.logger.info("Undo: removing shape with ID #{shape_id} at #{timestamp}") shapes.each do |shape| next unless shape[:id] == shape_id + shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) end else @@ -703,6 +716,7 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # times to. shapes.each do |shape| next unless shape[:shape_unique_id] == undo_shape[:shape_unique_id] + shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) end else @@ -736,8 +750,8 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times full_clear ? BigBlueButton.logger.info('Clear: removing all shapes') : BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") shapes.each do |shape| - if full_clear || (user_id == shape[:user_id]) - shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) + if (full_clear || (user_id == shape[:user_id])) && (shape[:undo].nil? || (shape[:undo] > timestamp)) + shape[:undo] = timestamp end end end @@ -759,9 +773,11 @@ def events_get_image_info(slide) FileUtils.mkdir_p(File.dirname(image_path)) command = \ if slide[:deskshare] - ['convert', '-size', "#{@presentation_props['deskshare_output_width']}x#{@presentation_props['deskshare_output_height']}", 'xc:transparent', '-background', 'transparent', image_path] + ['convert', '-size', + "#{@presentation_props['deskshare_output_width']}x#{@presentation_props['deskshare_output_height']}", 'xc:transparent', '-background', 'transparent', image_path,] else - ['convert', '-size', '1600x1200', 'xc:transparent', '-background', 'transparent', '-quality', '90', '+dither', '-depth', '8', '-colors', '256', image_path] + ['convert', '-size', '1600x1200', 'xc:transparent', '-background', 'transparent', '-quality', '90', '+dither', + '-depth', '8', '-colors', '256', image_path,] end BigBlueButton.exec_ret(*command) || raise("Unable to generate blank image for #{image_path}") end @@ -784,8 +800,8 @@ def process_presentation(package_dir) version: '1.1', viewBox: '0 0 800 600') - panzooms_rec = Builder::XmlMarkup.new(:indent => 2, margin: 1) - cursors_rec = Builder::XmlMarkup.new(:indent => 2, margin: 1) + panzooms_rec = Builder::XmlMarkup.new(indent: 2, margin: 1) + cursors_rec = Builder::XmlMarkup.new(indent: 2, margin: 1) # Current presentation/slide state current_presentation_slide = {} @@ -816,7 +832,7 @@ def process_presentation(package_dir) events_xml.xpath('/recording/event').each do |event| eventname = event['eventname'] last_timestamp = timestamp = - (translate_timestamp(event['timestamp']) / 1000.0).round(1) + (translate_timestamp(event['timestamp']) / 1000.0).round(1) # Make sure to add initial entries to the slide & panzoom lists slide_changed = slides.empty? @@ -908,7 +924,7 @@ def process_presentation(package_dir) presentation: current_presentation, slide: current_slide, in: timestamp, - deskshare: deskshare + deskshare: deskshare, } events_get_image_info(slide) slides << slide @@ -943,7 +959,7 @@ def process_presentation(package_dir) width: slide[:width], height: slide[:height], in: timestamp, - deskshare: deskshare + deskshare: deskshare, } panzooms << panzoom end @@ -951,6 +967,7 @@ def process_presentation(package_dir) # Perform cursor finalization next unless cursor_changed || panzoom_changed + unless (cursor_x >= 0) && (cursor_x <= 100) && cursor_y >= 0 && (cursor_y <= 100) cursor_visible = false @@ -974,7 +991,7 @@ def process_presentation(package_dir) x: cursor_x, y: cursor_y, panzoom: panzoom, - in: timestamp + in: timestamp, } cursors << cursor end @@ -1008,7 +1025,7 @@ end def process_chat_messages(events, bbb_props) BigBlueButton.logger.info('Processing chat events') # Create slides.xml and chat. - xml = Builder::XmlMarkup.new(:indent => 2) + xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! xml.popcorn do BigBlueButton::Events.get_chat_events(events, @meeting_start.to_i, @meeting_end.to_i, bbb_props).each do |chat| @@ -1017,7 +1034,7 @@ def process_chat_messages(events, bbb_props) direction: 'down', name: chat[:sender], message: chat[:message], - target: 'chat' + target: 'chat', } chattimeline[:out] = (chat[:out] / 1000.0).round(1) unless chat[:out].nil? xml.chattimeline(**chattimeline) @@ -1031,14 +1048,15 @@ def process_deskshare_events(events) BigBlueButton.logger.info('Processing deskshare events') deskshare_matched_events = BigBlueButton::Events.get_matched_start_and_stop_deskshare_events(events) - @deskshare_xml = Builder::XmlMarkup.new(:indent => 2) + @deskshare_xml = Builder::XmlMarkup.new(indent: 2) @deskshare_xml.instruct! - @deskshare_xml.recording('id' => 'deskshare_events') { + @deskshare_xml.recording('id' => 'deskshare_events') do deskshare_matched_events.each do |event| start_timestamp = (translate_timestamp(event[:start_timestamp].to_f) / 1000).round(1) stop_timestamp = (translate_timestamp(event[:stop_timestamp].to_f) / 1000).round(1) next unless start_timestamp != stop_timestamp + video_info = BigBlueButton::EDL::Video.video_info("#{@deskshare_dir}/#{event[:stream]}") unless video_info[:video] BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") @@ -1046,11 +1064,11 @@ def process_deskshare_events(events) end video_width, video_height = get_deskshare_video_dimension(event[:stream]) @deskshare_xml.event(start_timestamp: start_timestamp, - stop_timestamp: stop_timestamp, - video_width: video_width, - video_height: video_height) + stop_timestamp: stop_timestamp, + video_width: video_width, + video_height: video_height) end - } + end end def get_poll_question(event) @@ -1111,13 +1129,14 @@ def process_poll_events(events, package_dir) @rec_events.each do |re| events.xpath("recording/event[@eventname='PollPublishedRecordEvent']").each do |event| next unless (event[:timestamp].to_i >= re[:start_timestamp]) && (event[:timestamp].to_i <= re[:stop_timestamp]) + published_polls << { timestamp: (translate_timestamp(event[:timestamp]) / 1000).to_i, type: get_poll_type(events, event), question: get_poll_question(event), answers: get_poll_answers(event), respondents: get_poll_respondents(event), - responders: get_poll_responders(event) + responders: get_poll_responders(event), } end end @@ -1140,13 +1159,14 @@ def process_external_video_events(_events, package_dir) timestamp = (translate_timestamp(event[:start_timestamp]) / 1000).to_i # do not add same external_video twice next unless external_videos.find { |ev| ev[:timestamp] == timestamp }.nil? - if ((event[:start_timestamp] >= re[:start_timestamp]) && (event[:start_timestamp] <= re[:stop_timestamp])) || - ((event[:start_timestamp] < re[:start_timestamp]) && (event[:stop_timestamp] >= re[:start_timestamp])) - external_videos << { - timestamp: timestamp, - external_video_url: event[:external_video_url] - } - end + + next unless ((event[:start_timestamp] >= re[:start_timestamp]) && (event[:start_timestamp] <= re[:stop_timestamp])) || + ((event[:start_timestamp] < re[:start_timestamp]) && (event[:stop_timestamp] >= re[:start_timestamp])) + + external_videos << { + timestamp: timestamp, + external_video_url: event[:external_video_url], + } end end @@ -1158,8 +1178,8 @@ end @cursor_xml_filename = 'cursor.xml' @deskshare_xml_filename = 'deskshare.xml' -opts = Optimist::options do - opt :meeting_id, "Meeting id to archive", :default => '58f4a6b3-cd07-444d-8564-59116cb53974', :type => String +opts = Optimist.options do + opt :meeting_id, 'Meeting id to archive', default: '58f4a6b3-cd07-444d-8564-59116cb53974', type: String end @meeting_id = opts[:meeting_id] @@ -1310,7 +1330,9 @@ begin xml.images do presentation[:slides].each do |key, val| attributes = { width: '176', height: '136', alt: !val[:alt].nil? ? (val[:alt]).to_s : '' } - xml.image(attributes) { xml.text("#{playback_protocol}://#{playback_host}/presentation/#{@meeting_id}/presentation/#{presentation[:id]}/thumbnails/thumb-#{key}.png") } + xml.image(attributes) do + xml.text("#{playback_protocol}://#{playback_host}/presentation/#{@meeting_id}/presentation/#{presentation[:id]}/thumbnails/thumb-#{key}.png") + end end end end @@ -1359,31 +1381,34 @@ begin FileUtils.cp_r(package_dir, publish_dir) # Copy all the files. BigBlueButton.logger.info('Finished publishing script presentation.rb successfully.') - # TODO: remove comments - # BigBlueButton.logger.info("Removing processed files.") - # FileUtils.rm_r(Dir.glob("#{@process_dir}/*")) + BigBlueButton.logger.info('Removing processed files.') + FileUtils.rm_r(Dir.glob("#{@process_dir}/*")) BigBlueButton.logger.info('Removing published files.') FileUtils.rm_r(Dir.glob("#{target_dir}/*")) - #rescue StandardError => e - #BigBlueButton.logger.error(e.message) - #e.backtrace.each do |traceline| - #BigBlueButton.logger.error(traceline) - #end - #exit 1 + rescue StandardError => e + BigBlueButton.logger.error(e.message) + e.backtrace.each do |traceline| + BigBlueButton.logger.error(traceline) + end + exit 1 + end + File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.done", 'w') do |file| + file.write("Published #{@meeting_id}") end - File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.done", 'w') { |file| file.write("Published #{@meeting_id}") } else BigBlueButton.logger.info("#{target_dir} is already there") end end -#rescue StandardError => e - #BigBlueButton.logger.error(e.message) - #e.backtrace.each do |traceline| - # BigBlueButton.logger.error(traceline) - #end - #File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') { |file| file.write("Failed Publishing #{@meeting_id}") } +rescue StandardError => e + BigBlueButton.logger.error(e.message) + e.backtrace.each do |traceline| + BigBlueButton.logger.error(traceline) + end + File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') do |file| + file.write("Failed Publishing #{@meeting_id}") + end - #exit 1 + exit 1 end From 6dd50086fb8e4904bc05cbd286e0c82ec3644461 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Sat, 18 Dec 2021 20:50:56 +0100 Subject: [PATCH 15/26] Reek: remove duplicate method calls --- .../scripts/publish/presentation.rb | 265 ++++++++++-------- 1 file changed, 150 insertions(+), 115 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 90fcdd3d0a..9d8ff2ebff 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -23,10 +23,10 @@ _performance_start = Time.now # For DEVELOPMENT # Allows us to run the script manually -require File.expand_path('../../../core/lib/recordandplayback', __dir__) +# require File.expand_path('../../../core/lib/recordandplayback', __dir__) # For PRODUCTION -# require File.expand_path('../../../lib/recordandplayback', __FILE__) +require File.expand_path('../../../lib/recordandplayback', __dir__) require 'rubygems' require 'optimist' @@ -44,19 +44,17 @@ bbb_props = BigBlueButton.read_props @magic_mystery_number = 2 def scale_to_deskshare_video(width, height) - deskshare_video_height = @presentation_props['deskshare_output_height'].to_f - deskshare_video_width = @presentation_props['deskshare_output_height'].to_f + deskshare_video_height = deskshare_video_width = @presentation_props['deskshare_output_height'].to_f scale = [deskshare_video_width / width, deskshare_video_height / height] - video_width = width * scale.min - video_height = height * scale.min + video_width = width * (scale_min = scale.min) + video_height = height * scale_min [video_width.floor, video_height.floor] end def get_deskshare_video_dimension(deskshare_stream_name) - video_width = @presentation_props['deskshare_output_height'].to_f - video_height = @presentation_props['deskshare_output_height'].to_f + video_width = video_height = @presentation_props['deskshare_output_height'].to_f deskshare_video_filename = "#{@deskshare_dir}/#{deskshare_stream_name}" if File.exist?(deskshare_video_filename) @@ -77,11 +75,14 @@ def calculate_record_events_offset accumulated_duration = 0 previous_stop_recording = @meeting_start.to_f @rec_events.each do |event| - event[:offset] = event[:start_timestamp] - accumulated_duration - event[:duration] = event[:stop_timestamp] - event[:start_timestamp] + event_start = event[:start_timestamp] + event_stop = event[:stop_timestamp] + + event[:offset] = event_start - accumulated_duration + event[:duration] = event_stop - event_start event[:accumulated_duration] = accumulated_duration - previous_stop_recording = event[:stop_timestamp] + previous_stop_recording = event_stop accumulated_duration += event[:duration] end end @@ -92,7 +93,7 @@ end # def translate_timestamp(timestamp) new_timestamp = translate_timestamp_helper(timestamp.to_f).to_f - BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f - @meeting_start.to_f}, new value=#{new_timestamp}") + # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f - @meeting_start.to_f}, new value=#{new_timestamp}") new_timestamp end @@ -101,15 +102,17 @@ end # def translate_timestamp_helper(timestamp) @rec_events.each do |event| + start_timestamp = event[:start_timestamp] # if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording - return event[:start_timestamp] - event[:offset] if timestamp <= event[:start_timestamp] + return start_timestamp - event[:offset] if timestamp <= start_timestamp # if the timestamp is during the recording period, it is just translated to the new one using the offset - return timestamp - event[:offset] if (timestamp > event[:start_timestamp]) && (timestamp <= event[:stop_timestamp]) + return timestamp - event[:offset] if (timestamp > start_timestamp) && (timestamp <= event[:stop_timestamp]) end # if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp - timestamp - @rec_events.last[:offset] + @rec_events.last[:duration] + last_rec_event = @rec_events.last + timestamp - last_rec_event[:offset] + last_rec_event[:duration] end def color_to_hex(color) @@ -126,38 +129,40 @@ def shape_scale_height(slide, y) end def shape_thickness(slide, shape) - if !shape[:thickness_percent].nil? - shape_scale_width(slide, shape[:thickness_percent]) + if !(shape_thickness_percent = shape[:thickness_percent]).nil? + shape_scale_width(slide, + shape_thickness_percent) else shape[:thickness] end end def svg_render_shape_pencil(g, slide, shape) - g['shape'] = "pencil#{shape[:shape_unique_id]}" + g['shape'] = "pencil#{shape_unique_id = shape[:shape_unique_id]}" doc = g.document - - if shape[:data_points].length < 2 - BigBlueButton.logger.warn("Pencil #{shape[:shape_unique_id]} doesn't have enough points") + data_points = shape[:data_points] + data_points_length = data_points.length + if data_points_length < 2 + BigBlueButton.logger.warn("Pencil #{shape_unique_id} doesn't have enough points") return end - if shape[:data_points].length == 2 - # BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing single point") + if data_points_length == 2 + # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing single point") g['style'] = "stroke:none;fill:##{shape[:color]};visibility:hidden" circle = doc.create_element('circle', - cx: shape_scale_width(slide, shape[:data_points][0]), - cy: shape_scale_height(slide, shape[:data_points][1]), + cx: shape_scale_width(slide, data_points[0]), + cy: shape_scale_height(slide, data_points[1]), r: (shape_thickness(slide, shape) / 2.0).round(5)) g << circle else path = [] - data_points = shape[:data_points].each + data_points = data_points.each - if !shape[:commands].nil? - # BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing from command string (#{shape[:commands].length} commands)") - shape[:commands].each do |command| + if !(shape_commands = shape[:commands]).nil? + # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing from command string (#{shape_commands.length} commands)") + shape_commands.each do |command| case command when 1 # MOVE_TO x = shape_scale_width(slide, data_points.next) @@ -186,7 +191,7 @@ def svg_render_shape_pencil(g, slide, shape) end end else - # BigBlueButton.logger.info("Pencil #{shape[:shape_unique_id]}: Drawing simple line (#{shape[:data_points].length / 2} points)") + # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing simple line (#{data_points_length / 2} points)") x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) path << "M#{x} #{y}" @@ -226,8 +231,8 @@ def svg_render_shape_line(g, slide, shape) end def stroke_attributes(slide, shape) - "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, - shape)};visibility:hidden;fill:#{shape[:fill] ? "##{shape[:color]}" : 'none'}" + "stroke:##{shape_color = shape[:color]};stroke-width:#{shape_thickness(slide, + shape)};visibility:hidden;fill:#{shape[:fill] ? "##{shape_color}" : 'none'}" end def svg_render_shape_rect(g, slide, shape) @@ -389,23 +394,23 @@ def svg_render_shape_poll(g, slide, shape) end def svg_render_shape(canvas, slide, shape, image_id) - if shape[:in] == shape[:out] + if (shape_in = shape[:in]) == (shape_out = shape[:out]) BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is never shown (duration rounds to 0)") return end - if (shape[:in] >= slide[:out]) || - (!shape[:out].nil? && (shape[:out] <= slide[:in])) + if (shape_in >= slide[:out]) || + (!shape_out.nil? && (shape_out <= slide[:in])) BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is not visible during image time span") return end - # BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} Type #{shape[:type]} from #{shape[:in]} to #{shape[:out]} undo #{shape[:undo]}") + # BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} Type #{shape[:type]} from #{shape_in} to #{shape_out} undo #{shape[:undo]}") doc = canvas.document g = doc.create_element('g', id: "image#{image_id}-draw#{shape[:shape_id]}", class: 'shape', - timestamp: shape[:in], undo: (shape[:undo].nil? ? -1 : shape[:undo])) + timestamp: shape_in, undo: (shape[:undo].nil? ? -1 : shape[:undo])) case shape[:type] when 'pencil' @@ -433,34 +438,36 @@ end @svg_image_id = 1 def svg_render_image(svg, slide, shapes) - if slide[:in] == slide[:out] - BigBlueButton.logger.info("Presentation #{slide[:presentation]} Slide #{slide[:slide]} is never shown (duration rounds to 0)") + slide_number = slide[:slide] + presentation = slide[:presentation] + + if (slide_in = slide[:in]) == (slide_out = slide[:out]) + BigBlueButton.logger.info("Presentation #{presentation} Slide #{slide_number} is never shown (duration rounds to 0)") return end image_id = @svg_image_id @svg_image_id += 1 + slide_deskshare = slide[:deskshare] - BigBlueButton.logger.info("Image #{image_id}: Presentation #{slide[:presentation]} Slide #{slide[:slide]} Deskshare #{slide[:deskshare]} from #{slide[:in]} to #{slide[:out]}") + BigBlueButton.logger.info("Image #{image_id}: Presentation #{presentation} Slide #{slide_number} Deskshare #{slide_deskshare} from #{slide_in} to #{slide_out}") doc = svg.document image = doc.create_element('image', id: "image#{image_id}", class: 'slide', - in: slide[:in], out: slide[:out], + in: slide_in, out: slide_out, 'xlink:href' => slide[:src], width: slide[:width], height: slide[:height], x: 0, y: 0, style: 'visibility:hidden') - image['text'] = slide[:text] unless slide[:text].nil? + unless (slide_text = slide[:text]).nil? + image['text'] = slide_text + end svg << image - if slide[:deskshare] || - shapes[slide[:presentation]].nil? || - shapes[slide[:presentation]][slide[:slide]].nil? + if slide_deskshare || (presentation_shapes = shapes[presentation]).nil? || (shapes = presentation_shapes[slide_number]).nil? return end - shapes = shapes[slide[:presentation]][slide[:slide]] - canvas = doc.create_element('g', class: 'canvas', id: "canvas#{image_id}", image: "image#{image_id}", display: 'none') @@ -489,12 +496,12 @@ def panzoom_viewbox(panzoom) end def panzooms_emit_event(rec, panzoom) - if panzoom[:in] == panzoom[:out] + if (panzoom_in = panzoom[:in]) == panzoom[:out] BigBlueButton.logger.info('Panzoom: not emitting, duration rounds to 0') return end - rec.event(timestamp: panzoom[:in]) do + rec.event(timestamp: panzoom_in) do x, y, w, h = panzoom_viewbox(panzoom) rec.viewBox("#{x} #{y} #{w} #{h}") # BigBlueButton.logger.info("Panzoom viewbox #{x} #{y} #{w} #{h}" at #{panzoom[:in]}") @@ -502,12 +509,12 @@ def panzooms_emit_event(rec, panzoom) end def cursors_emit_event(rec, cursor) - if cursor[:in] == cursor[:out] + if (cursor_in = cursor[:in]) == cursor[:out] # BigBlueButton.logger.info('Cursor: not emitting, duration rounds to 0') return end - rec.event(timestamp: cursor[:in]) do + rec.event(timestamp: cursor_in) do panzoom = cursor[:panzoom] if cursor[:visible] if @version_atleast_2_0_0 @@ -517,18 +524,14 @@ def cursors_emit_event(rec, cursor) (panzoom[:width_ratio] / 100.0)).round(5) y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * @magic_mystery_number / 100.0)) / (panzoom[:height_ratio] / 100.0)).round(5) - if x.negative? || (x > 1) || y.negative? || (y > 1) - x = -1.0 - y = -1.0 - end + x = y = -1.0 if x.negative? || (x > 1) || y.negative? || (y > 1) else # Cursor position is relative to the visible area x = cursor[:x].round(5) y = cursor[:y].round(5) end else - x = -1.0 - y = -1.0 + x = y = -1.0 end rec.cursor("#{x} #{y}") @@ -574,25 +577,28 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape = {} # Common properties shape[:in] = timestamp - shape[:type] = event.at_xpath('type').text - shape[:data_points] = event.at_xpath('dataPoints').text.split(',').map(&:to_f) + shape_type = shape[:type] = event.at_xpath('type').text + shape_data_points = shape[:data_points] = event.at_xpath('dataPoints').text.split(',').map(&:to_f) # These can be missing in old BBB versions, there are fallbacks user_id = event.at_xpath('userId') shape[:user_id] = user_id.text unless user_id.nil? - shape_id = event.at_xpath('id') - shape[:id] = shape_id.text unless shape_id.nil? + + unless (shape_id = event.at_xpath('id').text).nil? + shape[:id] = shape_id + end + status = event.at_xpath('status') - shape[:status] = status.text unless status.nil? - shape[:shape_id] = @svg_shape_id + shape_status = shape[:status] = status.text unless status.nil? + draw_id = shape[:shape_id] = @svg_shape_id @svg_shape_id += 1 # Some shape-specific properties - if %w[ellipse line pencil rectangle triangle].include?(shape[:type]) + if %w[ellipse line pencil rectangle triangle].include?(shape_type) shape[:color] = color_to_hex(event.at_xpath('color').text) thickness = event.at_xpath('thickness') unless thickness - BigBlueButton.logger.warn("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} is missing thickness") + BigBlueButton.logger.warn("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} is missing thickness") return end if @version_atleast_2_0_0 @@ -601,13 +607,13 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:thickness] = thickness.text.to_i end end - if %w[ellipse rectangle triangle].include?(shape[:type]) + if %w[ellipse rectangle triangle].include?(shape_type) fill = event.at_xpath('fill') fill = fill.nil? ? 'false' : fill.text shape[:fill] = fill =~ /true/ ? true : false end - case shape[:type] + case shape_type when 'rectangle' square = event.at_xpath('square') shape[:square] = (square.text == 'true') unless square.nil? @@ -627,7 +633,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times calced_font_size = event.at_xpath('calcedFontSize') unless calced_font_size - BigBlueButton.logger.warn("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} is missing calcedFontSize") + BigBlueButton.logger.warn("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} is missing calcedFontSize") return end @@ -640,20 +646,21 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times # Find the previous shape, for updates prev_shape = nil - if !shape[:id].nil? + if !shape_id.nil? # If we have a shape ID, look up the previous shape by ID # Don't look for updates if the drawing has ended - unless shape[:status] == 'DRAW_END' - prev_shape_pos = shapes.rindex { |s| s[:id] == shape[:id] } + unless shape_status == 'DRAW_END' + prev_shape_pos = shapes.rindex { |s| s[:id] == shape_id } prev_shape = prev_shape_pos.nil? ? nil : shapes[prev_shape_pos] end else # No shape ID, so do heuristic matching. If the previous shape had the # same type and same first two data points, update it. last_shape = shapes.last - if (last_shape[:type] == shape[:type]) && - (last_shape[:data_points][0] == shape[:data_points][0]) && - (last_shape[:data_points][1] == shape[:data_points][1]) + last_shape_data_points = last_shape[:data_points] + if (last_shape[:type] == shape_type) && + (last_shape_data_points[0] == shape_data_points[0]) && + (last_shape_data_points[1] == shape_data_points[1]) prev_shape = last_shape end end @@ -661,17 +668,17 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times prev_shape[:out] = timestamp shape[:shape_unique_id] = prev_shape[:shape_unique_id] - if (shape[:type] == 'pencil') && (shape[:status] == 'DRAW_UPDATE') + if (shape_type == 'pencil') && (shape_status == 'DRAW_UPDATE') # BigBlueButton 2.0 quirk - 'DRAW_UPDATE' events on pencil tool only # include newly added points, rather than the full list. - shape[:data_points] = prev_shape[:data_points] + shape[:data_points] + shape[:data_points] = prev_shape[:data_points] + shape_data_points end else shape[:shape_unique_id] = @svg_shape_unique_id @svg_shape_unique_id += 1 end - # BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} ID #{shape[:id]} Type #{shape[:type]}") + # BigBlueButton.logger.info("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} Type #{shape[:type]}") shapes << shape end @@ -686,7 +693,8 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # Newer undo messages have the shape id, making this a lot easier shape_id = event.at_xpath('shapeId') - shape_id = shape_id.text unless shape_id.nil? + shape_id_nil = shape_id.nil? + shape_id = shape_id.text unless shape_id_nil # Set up the shapes data structures if needed shapes[presentation] = {} if shapes[presentation].nil? @@ -695,14 +703,14 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # We only need to deal with shapes for this slide shapes = shapes[presentation][slide] - if !shape_id.nil? + if !shape_id_nil # If we have the shape id, we simply have to update the undo time on # all the shapes with that id. BigBlueButton.logger.info("Undo: removing shape with ID #{shape_id} at #{timestamp}") shapes.each do |shape| next unless shape[:id] == shape_id - shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) + shape[:undo] = timestamp if (shape_undo = shape[:undo]).nil? || (shape_undo > timestamp) end else # The undo command removes the most recently added shape that has not @@ -710,14 +718,15 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest undo_pos = shapes.rindex { |s| s[:undo].nil? } undo_shape = undo_pos.nil? ? nil : shapes[undo_pos] if !undo_shape.nil? - BigBlueButton.logger.info("Undo: removing Shape #{undo_shape[:shape_unique_id]} at #{timestamp}") + undo_shape_unique_id = undo_shape[:shape_unique_id] + BigBlueButton.logger.info("Undo: removing Shape #{undo_shape_unique_id} at #{timestamp}") # We have an id number assigned to associate all the updated versions # of the same shape. Use that to determine which shapes to apply undo # times to. shapes.each do |shape| - next unless shape[:shape_unique_id] == undo_shape[:shape_unique_id] + next unless shape[:shape_unique_id] == undo_shape_unique_id - shape[:undo] = timestamp if shape[:undo].nil? || (shape[:undo] > timestamp) + shape[:undo] = timestamp if (shape_undo = shape[:undo]).nil? || (shape_undo > timestamp) end else BigBlueButton.logger.info('Undo: no applicable shapes found') @@ -750,20 +759,20 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times full_clear ? BigBlueButton.logger.info('Clear: removing all shapes') : BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") shapes.each do |shape| - if (full_clear || (user_id == shape[:user_id])) && (shape[:undo].nil? || (shape[:undo] > timestamp)) + if (full_clear || (user_id == shape[:user_id])) && ((shape_undo = shape[:undo]).nil? || (shape_undo > timestamp)) shape[:undo] = timestamp end end end def events_get_image_info(slide) - if slide[:deskshare] + if (slide_deskshare = slide[:deskshare]) slide[:src] = 'presentation/deskshare.png' - elsif slide[:presentation] == '' + elsif (slide_presentation = slide[:presentation]) == '' slide[:src] = 'presentation/logo.png' else - slide[:src] = "presentation/#{slide[:presentation]}/slide-#{slide[:slide] + 1}.png" - slide[:text] = "presentation/#{slide[:presentation]}/textfiles/slide-#{slide[:slide] + 1}.txt" + slide[:src] = "presentation/#{slide_presentation}/slide-#{slide_nr = slide[:slide] + 1}.png" + slide[:text] = "presentation/#{slide_presentation}/textfiles/slide-#{slide_nr}.txt" end image_path = "#{@process_dir}/#{slide[:src]}" @@ -772,7 +781,7 @@ def events_get_image_info(slide) # Emergency last-ditch blank image creation FileUtils.mkdir_p(File.dirname(image_path)) command = \ - if slide[:deskshare] + if slide_deskshare ['convert', '-size', "#{@presentation_props['deskshare_output_width']}x#{@presentation_props['deskshare_output_height']}", 'xc:transparent', '-background', 'transparent', image_path,] else @@ -907,14 +916,16 @@ def process_presentation(package_dir) # Perform slide finalization if slide_changed slide = slides.last - if !slide.nil? && + slide_nil = slide.nil? + + if !slide_nil && (slide[:presentation] == current_presentation) && (slide[:slide] == current_slide) && (slide[:deskshare] == deskshare) BigBlueButton.logger.info('Presentation/Slide: skipping, no changes') # slide_changed = false else - unless slide.nil? + unless slide_nil slide[:out] = timestamp svg_render_image(svg, slide, shapes) end @@ -935,22 +946,25 @@ def process_presentation(package_dir) if panzoom_changed slide = slides.last panzoom = panzooms.last - if !panzoom.nil? && + panzoom_nil = panzoom.nil? + slide_width = slide[:width] + slide_height = slide[:height] + if !panzoom_nil && (panzoom[:x_offset] == current_x_offset) && (panzoom[:y_offset] == current_y_offset) && (panzoom[:width_ratio] == current_width_ratio) && (panzoom[:height_ratio] == current_height_ratio) && - (panzoom[:width] == slide[:width]) && - (panzoom[:height] == slide[:height]) && + (panzoom[:width] == slide_width) && + (panzoom[:height] == slide_height) && (panzoom[:deskshare] == deskshare) BigBlueButton.logger.info('Panzoom: skipping, no changes') panzoom_changed = false else - unless panzoom.nil? + unless panzoom_nil panzoom[:out] = timestamp panzooms_emit_event(panzooms_rec, panzoom) end - BigBlueButton.logger.info("Panzoom: #{current_x_offset} #{current_y_offset} #{current_width_ratio} #{current_height_ratio} (#{slide[:width]}x#{slide[:height]})") + BigBlueButton.logger.info("Panzoom: #{current_x_offset} #{current_y_offset} #{current_width_ratio} #{current_height_ratio} (#{slide_width}x#{slide_height})") panzoom = { x_offset: current_x_offset, y_offset: current_y_offset, @@ -975,13 +989,14 @@ def process_presentation(package_dir) panzoom = panzooms.last cursor = cursors.last - if !cursor.nil? && + cursor_nil = cursor.nil? + if !cursor_nil && ((!cursor[:visible] && !cursor_visible) || ((cursor[:x] == cursor_x) && (cursor[:y] == cursor_y))) && !panzoom_changed BigBlueButton.logger.info('Cursor: skipping, no changes') else - unless cursor.nil? + unless cursor_nil cursor[:out] = timestamp cursors_emit_event(cursors_rec, cursor) end @@ -1036,7 +1051,10 @@ def process_chat_messages(events, bbb_props) message: chat[:message], target: 'chat', } - chattimeline[:out] = (chat[:out] / 1000.0).round(1) unless chat[:out].nil? + unless (chat_out = chat[:out]).nil? + chattimeline[:out] = (chat_out / 1000.0).round(1) + end + xml.chattimeline(**chattimeline) end end @@ -1057,12 +1075,12 @@ def process_deskshare_events(events) stop_timestamp = (translate_timestamp(event[:stop_timestamp].to_f) / 1000).round(1) next unless start_timestamp != stop_timestamp - video_info = BigBlueButton::EDL::Video.video_info("#{@deskshare_dir}/#{event[:stream]}") + video_info = BigBlueButton::EDL::Video.video_info("#{@deskshare_dir}/#{event_stream = event[:stream]}") unless video_info[:video] - BigBlueButton.logger.warn("#{event[:stream]} is not a valid video file, skipping...") + BigBlueButton.logger.warn("#{event_stream} is not a valid video file, skipping...") next end - video_width, video_height = get_deskshare_video_dimension(event[:stream]) + video_width, video_height = get_deskshare_video_dimension(event_stream) @deskshare_xml.event(start_timestamp: start_timestamp, stop_timestamp: stop_timestamp, video_width: video_width, @@ -1073,35 +1091,45 @@ end def get_poll_question(event) question = '' - question = event.at_xpath('question').text unless event.at_xpath('question').nil? + unless (question_event = event.at_xpath('question')).nil? + question = question_event.text + end question end def get_poll_answers(event) answers = [] - answers = JSON.parse(event.at_xpath('answers').content) unless event.at_xpath('answers').nil? + unless (answers_event = event.at_xpath('answers')).nil? + answers = JSON.parse(answers_event.content) + end answers end def get_poll_respondents(event) respondents = 0 - respondents = event.at_xpath('numRespondents').text.to_i unless event.at_xpath('numRespondents').nil? + unless (num_respondents = event.at_xpath('numRespondents')).nil? + respondents = num_respondents.text.to_i + end respondents end def get_poll_responders(event) responders = 0 - responders = event.at_xpath('numResponders').text.to_i unless event.at_xpath('numResponders').nil? + unless (num_responders = event.at_xpath('numResponders')).nil? + responders = num_responders.text.to_i + end responders end def get_poll_id(event) id = '' - id = event.at_xpath('pollId').text unless event.at_xpath('pollId').nil? + unless (poll_id_event = event.at_xpath('pollId')).nil? + id = poll_id_event.text + end id end @@ -1128,10 +1156,10 @@ def process_poll_events(events, package_dir) published_polls = [] @rec_events.each do |re| events.xpath("recording/event[@eventname='PollPublishedRecordEvent']").each do |event| - next unless (event[:timestamp].to_i >= re[:start_timestamp]) && (event[:timestamp].to_i <= re[:stop_timestamp]) + next unless ((timestamp = event[:timestamp]).to_i >= re[:start_timestamp]) && (timestamp.to_i <= re[:stop_timestamp]) published_polls << { - timestamp: (translate_timestamp(event[:timestamp]) / 1000).to_i, + timestamp: (translate_timestamp(timestamp) / 1000).to_i, type: get_poll_type(events, event), question: get_poll_question(event), answers: get_poll_answers(event), @@ -1156,12 +1184,15 @@ def process_external_video_events(_events, package_dir) @rec_events.each do |re| external_video_events.each do |event| # BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}") - timestamp = (translate_timestamp(event[:start_timestamp]) / 1000).to_i + start_timestamp = event[:start_timestamp] + timestamp = (translate_timestamp(start_timestamp) / 1000).to_i # do not add same external_video twice next unless external_videos.find { |ev| ev[:timestamp] == timestamp }.nil? - next unless ((event[:start_timestamp] >= re[:start_timestamp]) && (event[:start_timestamp] <= re[:stop_timestamp])) || - ((event[:start_timestamp] < re[:start_timestamp]) && (event[:stop_timestamp] >= re[:start_timestamp])) + re_start_timestamp = re[:start_timestamp] + re_stop_timestamp = re[:stop_timestamp] + next unless ((start_timestamp >= re_start_timestamp) && (start_timestamp <= re_stop_timestamp)) || + ((start_timestamp < re_start_timestamp) && (re_stop_timestamp >= re_start_timestamp)) external_videos << { timestamp: timestamp, @@ -1183,13 +1214,13 @@ opts = Optimist.options do end @meeting_id = opts[:meeting_id] -puts @meeting_id +# puts "Meeting ID + Playback: #{@meeting_id}" match = /(.*)-(.*)/.match @meeting_id @meeting_id = match[1] @playback = match[2] -puts @meeting_id -puts @playback +# puts "Meeting ID: #{@meeting_id}" +# puts "Playback format: #{@playback}" begin if @playback == 'presentation' @@ -1199,13 +1230,17 @@ begin BigBlueButton.logger.info('Setting recording dir') recording_dir = bbb_props['recording_dir'] + BigBlueButton.logger.info('Setting process dir') @process_dir = "#{recording_dir}/process/presentation/#{@meeting_id}" + BigBlueButton.logger.info('Setting publish dir') publish_dir = @presentation_props['publish_dir'] + BigBlueButton.logger.info('Setting playback url info') playback_protocol = bbb_props['playback_protocol'] playback_host = bbb_props['playback_host'] + BigBlueButton.logger.info('Setting target dir') target_dir = "#{recording_dir}/publish/presentation/#{@meeting_id}" @deskshare_dir = "#{recording_dir}/raw/#{@meeting_id}/deskshare" From 5d40c427d533ae9389d4a7ee5e0296daa15945fa Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 20 Dec 2021 00:00:04 +0100 Subject: [PATCH 16/26] Flay: refactor sections of similar code --- .../scripts/publish/presentation.rb | 152 +++++++++--------- 1 file changed, 74 insertions(+), 78 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 9d8ff2ebff..7e2c180933 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -1,4 +1,3 @@ -# Set encoding to utf-8 # frozen_string_literal: false # @@ -23,10 +22,10 @@ _performance_start = Time.now # For DEVELOPMENT # Allows us to run the script manually -# require File.expand_path('../../../core/lib/recordandplayback', __dir__) +# require File.expand_path('../../../core/lib/recordandplayback', __FILE__) # For PRODUCTION -require File.expand_path('../../../lib/recordandplayback', __dir__) +require File.expand_path('../../../lib/recordandplayback', __FILE__) require 'rubygems' require 'optimist' @@ -164,14 +163,10 @@ def svg_render_shape_pencil(g, slide, shape) # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing from command string (#{shape_commands.length} commands)") shape_commands.each do |command| case command - when 1 # MOVE_TO + when 1, 2 # MOVE_TO, LINE_TO x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) - path.push("M#{x} #{y}") - when 2 # LINE_TO - x = shape_scale_width(slide, data_points.next) - y = shape_scale_height(slide, data_points.next) - path.push("L#{x} #{y}") + path.push("#{command.eql?(1) ? 'M' : 'L'}#{x} #{y}") when 3 # Q_CURVE_TO cx1 = shape_scale_width(slide, data_points.next) cy1 = shape_scale_height(slide, data_points.next) @@ -232,7 +227,7 @@ end def stroke_attributes(slide, shape) "stroke:##{shape_color = shape[:color]};stroke-width:#{shape_thickness(slide, - shape)};visibility:hidden;fill:#{shape[:fill] ? "##{shape_color}" : 'none'}" + shape)};visibility:hidden;fill:#{shape[:fill] ? "##{shape_color}" : 'none'}" end def svg_render_shape_rect(g, slide, shape) @@ -508,6 +503,10 @@ def panzooms_emit_event(rec, panzoom) end end +def convert_cursor_coordinate(cursor_coord, panzoom_offset, panzoom_ratio) + (((cursor_coord / 100.0) + (panzoom_offset * @magic_mystery_number / 100.0)) / (panzoom_ratio / 100.0)).round(5) +end + def cursors_emit_event(rec, cursor) if (cursor_in = cursor[:in]) == cursor[:out] # BigBlueButton.logger.info('Cursor: not emitting, duration rounds to 0') @@ -520,10 +519,8 @@ def cursors_emit_event(rec, cursor) if @version_atleast_2_0_0 # In BBB 2.0, the cursor now uses the same coordinate system as annotations # Use the panzoom information to convert it to be relative to viewbox - x = (((cursor[:x] / 100.0) + (panzoom[:x_offset] * @magic_mystery_number / 100.0)) / - (panzoom[:width_ratio] / 100.0)).round(5) - y = (((cursor[:y] / 100.0) + (panzoom[:y_offset] * @magic_mystery_number / 100.0)) / - (panzoom[:height_ratio] / 100.0)).round(5) + x = convert_cursor_coordinate(cursor[:x], panzoom[:x_offset], panzoom[:width_ratio]) + y = convert_cursor_coordinate(cursor[:y], panzoom[:y_offset], panzoom[:height_ratio]) x = y = -1.0 if x.negative? || (x > 1) || y.negative? || (y > 1) else # Cursor position is relative to the visible area @@ -583,7 +580,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times # These can be missing in old BBB versions, there are fallbacks user_id = event.at_xpath('userId') shape[:user_id] = user_id.text unless user_id.nil? - + unless (shape_id = event.at_xpath('id').text).nil? shape[:id] = shape_id end @@ -682,6 +679,14 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shapes << shape end +def set_undo_helper(shapes, key, id, timestamp) + shapes.each do |shape| + next unless shape[key] == id + + shape[:undo] = timestamp if (shape_undo = shape[:undo]).nil? || (shape_undo > timestamp) + end +end + def events_parse_undo(shapes, event, current_presentation, current_slide, timestamp) # Figure out what presentation+slide this undo is for, with fallbacks # for old BBB where this info isn't in the undo messages @@ -694,7 +699,7 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # Newer undo messages have the shape id, making this a lot easier shape_id = event.at_xpath('shapeId') shape_id_nil = shape_id.nil? - shape_id = shape_id.text unless shape_id_nil + shape_id = shape_id.text unless shape_id_nil # Set up the shapes data structures if needed shapes[presentation] = {} if shapes[presentation].nil? @@ -707,11 +712,8 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # If we have the shape id, we simply have to update the undo time on # all the shapes with that id. BigBlueButton.logger.info("Undo: removing shape with ID #{shape_id} at #{timestamp}") - shapes.each do |shape| - next unless shape[:id] == shape_id - shape[:undo] = timestamp if (shape_undo = shape[:undo]).nil? || (shape_undo > timestamp) - end + set_undo_helper(shapes, :id, shape_id, timestamp) else # The undo command removes the most recently added shape that has not # already been removed by another undo or clear. Find that shape. @@ -723,11 +725,7 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # We have an id number assigned to associate all the updated versions # of the same shape. Use that to determine which shapes to apply undo # times to. - shapes.each do |shape| - next unless shape[:shape_unique_id] == undo_shape_unique_id - - shape[:undo] = timestamp if (shape_undo = shape[:undo]).nil? || (shape_undo > timestamp) - end + set_undo_helper(shapes, :shape_unique_id, undo_shape_unique_id, timestamp) else BigBlueButton.logger.info('Undo: no applicable shapes found') end @@ -1101,7 +1099,7 @@ end def get_poll_answers(event) answers = [] unless (answers_event = event.at_xpath('answers')).nil? - answers = JSON.parse(answers_event.content) + answers = JSON.parse(answers_event.content) end answers @@ -1110,7 +1108,7 @@ end def get_poll_respondents(event) respondents = 0 unless (num_respondents = event.at_xpath('numRespondents')).nil? - respondents = num_respondents.text.to_i + respondents = num_respondents.text.to_i end respondents @@ -1119,7 +1117,7 @@ end def get_poll_responders(event) responders = 0 unless (num_responders = event.at_xpath('numResponders')).nil? - responders = num_responders.text.to_i + responders = num_responders.text.to_i end responders @@ -1128,7 +1126,7 @@ end def get_poll_id(event) id = '' unless (poll_id_event = event.at_xpath('pollId')).nil? - id = poll_id_event.text + id = poll_id_event.text end id @@ -1150,6 +1148,10 @@ def get_poll_type(events, published_poll_event) type end +def generate_json_file(package_dir, filename, contents) + File.open("#{package_dir}/#{filename}", 'w') { |f| f.puts(contents.to_json) } unless contents.empty? +end + def process_poll_events(events, package_dir) BigBlueButton.logger.info('Processing poll events') @@ -1169,7 +1171,7 @@ def process_poll_events(events, package_dir) end end - File.open("#{package_dir}/polls.json", 'w') { |f| f.puts(published_polls.to_json) } unless published_polls.empty? + generate_json_file(package_dir, 'polls.json', published_polls) end def process_external_video_events(_events, package_dir) @@ -1201,7 +1203,24 @@ def process_external_video_events(_events, package_dir) end end - File.open("#{package_dir}/external_videos.json", 'w') { |f| f.puts(external_videos.to_json) } unless external_videos.empty? + generate_json_file(package_dir, 'external_videos.json', external_videos) +end + +def generate_done_or_fail_file(success) + File.open("#{@recording_dir}/status/published/#{@meeting_id}-presentation#{success ? '.done' : '.fail'}", 'w') do |file| + file.write("#{success ? 'Published' : 'Failed publishing'} #{@meeting_id}") + end +end + +def copy_media_files_helper(media, media_files, package_dir) + BigBlueButton.logger.info("Making #{media} dir") + FileUtils.mkdir_p(media_dir = "#{package_dir}/#{media}") + + media_files.each do |media_file| + BigBlueButton.logger.info("Made #{media} dir - copying: #{media_file} to -> #{media_dir}") + FileUtils.cp(media_file, media_dir) + BigBlueButton.logger.info("Copied #{File.extname(media_file)} file") + end end @shapes_svg_filename = 'shapes.svg' @@ -1229,21 +1248,21 @@ begin BigBlueButton.logger = logger BigBlueButton.logger.info('Setting recording dir') - recording_dir = bbb_props['recording_dir'] + @recording_dir = bbb_props['recording_dir'] BigBlueButton.logger.info('Setting process dir') - @process_dir = "#{recording_dir}/process/presentation/#{@meeting_id}" - + @process_dir = "#{@recording_dir}/process/presentation/#{@meeting_id}" + BigBlueButton.logger.info('Setting publish dir') publish_dir = @presentation_props['publish_dir'] - + BigBlueButton.logger.info('Setting playback url info') playback_protocol = bbb_props['playback_protocol'] playback_host = bbb_props['playback_host'] - + BigBlueButton.logger.info('Setting target dir') - target_dir = "#{recording_dir}/publish/presentation/#{@meeting_id}" - @deskshare_dir = "#{recording_dir}/raw/#{@meeting_id}/deskshare" + target_dir = "#{@recording_dir}/publish/presentation/#{@meeting_id}" + @deskshare_dir = "#{@recording_dir}/raw/#{@meeting_id}/deskshare" if !FileTest.directory?(target_dir) BigBlueButton.logger.info('Making dir target_dir') @@ -1258,27 +1277,21 @@ begin video_files = Dir.glob("#{@process_dir}/webcams.{#{video_formats.join(',')}}") if !video_files.empty? - BigBlueButton.logger.info('Making video dir') - video_dir = "#{package_dir}/video" - FileUtils.mkdir_p video_dir - video_files.each do |video_file| - BigBlueButton.logger.info("Made video dir - copying: #{video_file} to -> #{video_dir}") - FileUtils.cp(video_file, video_dir) - BigBlueButton.logger.info("Copied #{File.extname(video_file)} file") - end + copy_media_files_helper('video', video_files, package_dir) else - audio_dir = "#{package_dir}/audio" - BigBlueButton.logger.info('Making audio dir') - FileUtils.mkdir_p audio_dir - BigBlueButton.logger.info("Made audio dir - copying: #{@process_dir}/audio.webm to -> #{audio_dir}") - FileUtils.cp("#{@process_dir}/audio.webm", audio_dir) - BigBlueButton.logger.info("Copied audio.webm file - copying: #{@process_dir}/audio.ogg to -> #{audio_dir}") - FileUtils.cp("#{@process_dir}/audio.ogg", audio_dir) - BigBlueButton.logger.info('Copied audio.ogg file') + copy_media_files_helper('audio', ["#{@process_dir}/audio.webm", "#{@process_dir}/audio.ogg"], package_dir) + end + + video_files = Dir.glob("#{@process_dir}/deskshare.{#{video_formats.join(',')}}") + if !video_files.empty? + copy_media_files_helper('deskshare', video_files, package_dir) + else + BigBlueButton.logger.info("Could not copy deskshares.webm: file doesn't exist") end if File.exist?("#{@process_dir}/captions.json") BigBlueButton.logger.info('Copying caption files') + FileUtils.cp("#{@process_dir}/captions.json", package_dir) Dir.glob("#{@process_dir}/caption_*.vtt").each do |caption| BigBlueButton.logger.debug(caption) @@ -1286,26 +1299,14 @@ begin end end - video_files = Dir.glob("#{@process_dir}/deskshare.{#{video_formats.join(',')}}") - if !video_files.empty? - BigBlueButton.logger.info('Making deskshare dir') - deskshare_dir = "#{package_dir}/deskshare" - FileUtils.mkdir_p deskshare_dir - video_files.each do |video_file| - BigBlueButton.logger.info("Made deskshare dir - copying: #{video_file} to -> #{deskshare_dir}") - FileUtils.cp(video_file, deskshare_dir) - BigBlueButton.logger.info("Copied #{File.extname(video_file)} file") - end - else - BigBlueButton.logger.info("Could not copy deskshares.webm: file doesn't exist") + if File.exist?(presentation_text = "#{@process_dir}/presentation_text.json") + FileUtils.cp(presentation_text, package_dir) end - if File.exist?("#{@process_dir}/presentation_text.json") - FileUtils.cp("#{@process_dir}/presentation_text.json", package_dir) + if File.exist?(notes = "#{@process_dir}/notes/notes.html") + FileUtils.cp(notes, package_dir) end - FileUtils.cp("#{@process_dir}/notes/notes.html", package_dir) if File.exist?("#{@process_dir}/notes/notes.html") - processing_time = File.read("#{@process_dir}/processing_time") @doc = Nokogiri::XML(File.read("#{@process_dir}/events.xml")) @@ -1408,7 +1409,7 @@ begin FileUtils.mkdir_p publish_dir unless FileTest.directory?(publish_dir) # Get raw size of presentation files - raw_dir = "#{recording_dir}/raw/#{@meeting_id}" + raw_dir = "#{@recording_dir}/raw/#{@meeting_id}" # After all the processing we'll add the published format and raw sizes to the metadata file BigBlueButton.add_raw_size_to_metadata(package_dir, raw_dir) BigBlueButton.add_playback_size_to_metadata(package_dir) @@ -1428,10 +1429,7 @@ begin end exit 1 end - File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.done", 'w') do |file| - file.write("Published #{@meeting_id}") - end - + generate_done_or_fail_file(true) else BigBlueButton.logger.info("#{target_dir} is already there") end @@ -1441,9 +1439,7 @@ rescue StandardError => e e.backtrace.each do |traceline| BigBlueButton.logger.error(traceline) end - File.open("#{recording_dir}/status/published/#{@meeting_id}-presentation.fail", 'w') do |file| - file.write("Failed Publishing #{@meeting_id}") - end + generate_done_or_fail_file(false) exit 1 end From 12dbd093faafe792baf31ada9a386badb8475fad Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Wed, 22 Dec 2021 14:25:45 +0100 Subject: [PATCH 17/26] Safe navigation operator for polls, ||= for shapes, shapes.dig --- .../scripts/publish/presentation.rb | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 7e2c180933..2403905f63 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -564,8 +564,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times slide = determine_slide_number(slide, current_slide) # Set up the shapes data structures if needed - shapes[presentation] = {} if shapes[presentation].nil? - shapes[presentation][slide] = [] if shapes[presentation][slide].nil? + shapes[presentation] ||= {} + shapes[presentation][slide] ||= [] # We only need to deal with shapes for this slide shapes = shapes[presentation][slide] @@ -1124,12 +1124,7 @@ def get_poll_responders(event) end def get_poll_id(event) - id = '' - unless (poll_id_event = event.at_xpath('pollId')).nil? - id = poll_id_event.text - end - - id + event.at_xpath('pollId')&.text || '' end def get_poll_type(events, published_poll_event) @@ -1417,11 +1412,8 @@ begin FileUtils.cp_r(package_dir, publish_dir) # Copy all the files. BigBlueButton.logger.info('Finished publishing script presentation.rb successfully.') - BigBlueButton.logger.info('Removing processed files.') - FileUtils.rm_r(Dir.glob("#{@process_dir}/*")) - - BigBlueButton.logger.info('Removing published files.') - FileUtils.rm_r(Dir.glob("#{target_dir}/*")) + BigBlueButton.logger.info('Removing processed and published files.') + FileUtils.rm_r([Dir.glob("#{@process_dir}/*"), Dir.glob("#{target_dir}/*")]) rescue StandardError => e BigBlueButton.logger.error(e.message) e.backtrace.each do |traceline| From a715ac520b442cf7df716f127b5e7a9a923fdf7d Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Wed, 22 Dec 2021 16:08:51 +0100 Subject: [PATCH 18/26] Remove translate_timestamp_helper, unused comments --- .../scripts/publish/presentation.rb | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 2403905f63..7621e2654b 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -22,10 +22,10 @@ _performance_start = Time.now # For DEVELOPMENT # Allows us to run the script manually -# require File.expand_path('../../../core/lib/recordandplayback', __FILE__) +# require File.expand_path('../../core/lib/recordandplayback', __dir__) # For PRODUCTION -require File.expand_path('../../../lib/recordandplayback', __FILE__) +require File.expand_path('../../lib/recordandplayback', __dir__) require 'rubygems' require 'optimist' @@ -86,32 +86,23 @@ def calculate_record_events_offset end end -# -# Translated an arbitrary Unix timestamp to the recording timestamp. This is the -# function that others will call -# -def translate_timestamp(timestamp) - new_timestamp = translate_timestamp_helper(timestamp.to_f).to_f - # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f - @meeting_start.to_f}, new value=#{new_timestamp}") - new_timestamp -end - # # Translated an arbitrary Unix timestamp to the recording timestamp # -def translate_timestamp_helper(timestamp) +def translate_timestamp(timestamp) + timestamp = timestamp.to_f @rec_events.each do |event| start_timestamp = event[:start_timestamp] # if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording - return start_timestamp - event[:offset] if timestamp <= start_timestamp + return (start_timestamp - event[:offset]).to_f if timestamp <= start_timestamp # if the timestamp is during the recording period, it is just translated to the new one using the offset - return timestamp - event[:offset] if (timestamp > start_timestamp) && (timestamp <= event[:stop_timestamp]) + return (timestamp - event[:offset]).to_f if (timestamp > start_timestamp) && (timestamp <= event[:stop_timestamp]) end # if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp last_rec_event = @rec_events.last - timestamp - last_rec_event[:offset] + last_rec_event[:duration] + (timestamp - last_rec_event[:offset] + last_rec_event[:duration]).to_f end def color_to_hex(color) From 1db68945994ae5deccb4ca7e38e0ce0aa0f9c526 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Wed, 22 Dec 2021 16:08:51 +0100 Subject: [PATCH 19/26] Remove translate_timestamp_helper, unused comments --- .../scripts/publish/presentation.rb | 41 +++++-------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 2403905f63..f6c4c3988b 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -22,10 +22,10 @@ _performance_start = Time.now # For DEVELOPMENT # Allows us to run the script manually -# require File.expand_path('../../../core/lib/recordandplayback', __FILE__) +# require File.expand_path('../../core/lib/recordandplayback', __dir__) # For PRODUCTION -require File.expand_path('../../../lib/recordandplayback', __FILE__) +require File.expand_path('../../lib/recordandplayback', __dir__) require 'rubygems' require 'optimist' @@ -86,32 +86,23 @@ def calculate_record_events_offset end end -# -# Translated an arbitrary Unix timestamp to the recording timestamp. This is the -# function that others will call -# -def translate_timestamp(timestamp) - new_timestamp = translate_timestamp_helper(timestamp.to_f).to_f - # BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f - @meeting_start.to_f}, new value=#{new_timestamp}") - new_timestamp -end - # # Translated an arbitrary Unix timestamp to the recording timestamp # -def translate_timestamp_helper(timestamp) +def translate_timestamp(timestamp) + timestamp = timestamp.to_f @rec_events.each do |event| start_timestamp = event[:start_timestamp] # if the timestamp comes before the start recording event, then the timestamp is translated to the moment it starts recording - return start_timestamp - event[:offset] if timestamp <= start_timestamp + return (start_timestamp - event[:offset]).to_f if timestamp <= start_timestamp # if the timestamp is during the recording period, it is just translated to the new one using the offset - return timestamp - event[:offset] if (timestamp > start_timestamp) && (timestamp <= event[:stop_timestamp]) + return (timestamp - event[:offset]).to_f if (timestamp > start_timestamp) && (timestamp <= event[:stop_timestamp]) end # if the timestamp comes after the last stop recording event, then the timestamp is translated to the last stop recording event timestamp last_rec_event = @rec_events.last - timestamp - last_rec_event[:offset] + last_rec_event[:duration] + (timestamp - last_rec_event[:offset] + last_rec_event[:duration]).to_f end def color_to_hex(color) @@ -243,7 +234,6 @@ def svg_render_shape_rect(g, slide, shape) y2 = shape_scale_height(slide, data_points[3]) width = (x2 - x1).abs - # height = (y2 - y1).abs if shape[:square] # Convert to a square, keeping aligned with the start point. @@ -459,9 +449,9 @@ def svg_render_image(svg, slide, shapes) end svg << image - if slide_deskshare || (presentation_shapes = shapes[presentation]).nil? || (shapes = presentation_shapes[slide_number]).nil? - return - end + return if slide_deskshare || shapes.dig(presentation, slide_number).nil? + + shapes = shapes[presentation][slide_number] canvas = doc.create_element('g', class: 'canvas', id: "canvas#{image_id}", @@ -1228,14 +1218,10 @@ opts = Optimist.options do end @meeting_id = opts[:meeting_id] -# puts "Meeting ID + Playback: #{@meeting_id}" match = /(.*)-(.*)/.match @meeting_id @meeting_id = match[1] @playback = match[2] -# puts "Meeting ID: #{@meeting_id}" -# puts "Playback format: #{@playback}" - begin if @playback == 'presentation' log_dir = bbb_props['log_dir'] @@ -1313,8 +1299,6 @@ begin recording_time = BigBlueButton::Events.get_recording_length(@doc) - # presentation_url = "/slides/" + @meeting_id + "/presentation" - @meeting_start = BigBlueButton::Events.first_event_timestamp(@doc) @meeting_end = BigBlueButton::Events.last_event_timestamp(@doc) @@ -1326,11 +1310,6 @@ begin ) BigBlueButton.logger.info('Creating metadata.xml') - # Get the real-time start and end timestamp - # match = /.*-(\d+)@/.match(@meeting_id) - # real_start_time = match[1] - # real_end_time = (real_start_time.to_i + (@meeting_end.to_i - @meeting_start.to_i)).to_s - #### INSTEAD OF CREATING THE WHOLE metadata.xml FILE AGAIN, ONLY ADD # Copy metadata.xml from process_dir FileUtils.cp("#{@process_dir}/metadata.xml", package_dir) From 824274e91cabb3f71166507d67f3dce9b3c01ea1 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Thu, 23 Dec 2021 23:39:37 +0100 Subject: [PATCH 20/26] Remove nil checks --- .../scripts/publish/presentation.rb | 111 +++++++----------- 1 file changed, 45 insertions(+), 66 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 90ea7d6d0e..4b350afb6b 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -119,12 +119,7 @@ def shape_scale_height(slide, y) end def shape_thickness(slide, shape) - if !(shape_thickness_percent = shape[:thickness_percent]).nil? - shape_scale_width(slide, - shape_thickness_percent) - else - shape[:thickness] - end + (shape_thickness_percent = shape[:thickness_percent]) ? shape_scale_width(slide, shape_thickness_percent) : shape[:thickness] end def svg_render_shape_pencil(g, slide, shape) @@ -150,7 +145,7 @@ def svg_render_shape_pencil(g, slide, shape) path = [] data_points = data_points.each - if !(shape_commands = shape[:commands]).nil? + if (shape_commands = shape[:commands]) # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing from command string (#{shape_commands.length} commands)") shape_commands.each do |command| case command @@ -384,8 +379,7 @@ def svg_render_shape(canvas, slide, shape, image_id) return end - if (shape_in >= slide[:out]) || - (!shape_out.nil? && (shape_out <= slide[:in])) + if (shape_in >= slide[:out]) || (shape_out && (shape_out <= slide[:in])) BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is not visible during image time span") return end @@ -444,12 +438,12 @@ def svg_render_image(svg, slide, shapes) 'xlink:href' => slide[:src], width: slide[:width], height: slide[:height], x: 0, y: 0, style: 'visibility:hidden') - unless (slide_text = slide[:text]).nil? + if (slide_text = slide[:text]) image['text'] = slide_text end svg << image - return if slide_deskshare || shapes.dig(presentation, slide_number).nil? + return if slide_deskshare || !shapes.dig(presentation, slide_number) shapes = shapes[presentation][slide_number] @@ -533,7 +527,7 @@ def determine_presentation(presentation, current_presentation) end def determine_slide_number(slide, current_slide) - return current_slide if slide.nil? + return current_slide if !slide slide = slide.text.to_i slide -= 1 unless @version_atleast_0_9_0 @@ -564,23 +558,22 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape_data_points = shape[:data_points] = event.at_xpath('dataPoints').text.split(',').map(&:to_f) # These can be missing in old BBB versions, there are fallbacks - user_id = event.at_xpath('userId') - shape[:user_id] = user_id.text unless user_id.nil? + user_id = event.at_xpath('userId')&.text + shape[:user_id] = user_id if user_id - unless (shape_id = event.at_xpath('id').text).nil? + if (shape_id = event.at_xpath('id')&.text) shape[:id] = shape_id end - status = event.at_xpath('status') - shape_status = shape[:status] = status.text unless status.nil? + status = event.at_xpath('status')&.text + shape_status = shape[:status] = status if status draw_id = shape[:shape_id] = @svg_shape_id @svg_shape_id += 1 # Some shape-specific properties if %w[ellipse line pencil rectangle triangle].include?(shape_type) shape[:color] = color_to_hex(event.at_xpath('color').text) - thickness = event.at_xpath('thickness') - unless thickness + unless (thickness = event.at_xpath('thickness')) BigBlueButton.logger.warn("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} is missing thickness") return end @@ -597,14 +590,14 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times case shape_type when 'rectangle' - square = event.at_xpath('square') - shape[:square] = (square.text == 'true') unless square.nil? + square = event.at_xpath('square')&.text + shape[:square] = (square == 'true') if square when 'ellipse' - circle = event.at_xpath('circle') - shape[:circle] = (circle.text == 'true') unless circle.nil? + circle = event.at_xpath('circle')&.text + shape[:circle] = (circle == 'true') if circle when 'pencil' - commands = event.at_xpath('commands') - shape[:commands] = commands.text.split(',').map(&:to_i) unless commands.nil? + commands = event.at_xpath('commands')&.text + shape[:commands] = commands.split(',').map(&:to_i) if commands when 'poll_result' shape[:num_responders] = event.at_xpath('num_responders').text.to_i shape[:num_respondents] = event.at_xpath('num_respondents').text.to_i @@ -613,8 +606,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:text_box_width] = event.at_xpath('textBoxWidth').text.to_f shape[:text_box_height] = event.at_xpath('textBoxHeight').text.to_f - calced_font_size = event.at_xpath('calcedFontSize') - unless calced_font_size + unless (calced_font_size = event.at_xpath('calcedFontSize')) BigBlueButton.logger.warn("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} is missing calcedFontSize") return end @@ -627,12 +619,12 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times # Find the previous shape, for updates prev_shape = nil - if !shape_id.nil? + if shape_id # If we have a shape ID, look up the previous shape by ID # Don't look for updates if the drawing has ended unless shape_status == 'DRAW_END' prev_shape_pos = shapes.rindex { |s| s[:id] == shape_id } - prev_shape = prev_shape_pos.nil? ? nil : shapes[prev_shape_pos] + prev_shape = prev_shape_pos ? shapes[prev_shape_pos] : nil end else # No shape ID, so do heuristic matching. If the previous shape had the @@ -645,7 +637,7 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times prev_shape = last_shape end end - if !prev_shape.nil? + if prev_shape prev_shape[:out] = timestamp shape[:shape_unique_id] = prev_shape[:shape_unique_id] @@ -667,23 +659,18 @@ def set_undo_helper(shapes, key, id, timestamp) shapes.each do |shape| next unless shape[key] == id - shape[:undo] = timestamp if (shape_undo = shape[:undo]).nil? || (shape_undo > timestamp) + shape[:undo] = timestamp if !(shape_undo = shape[:undo]) || (shape_undo > timestamp) end end def events_parse_undo(shapes, event, current_presentation, current_slide, timestamp) # Figure out what presentation+slide this undo is for, with fallbacks # for old BBB where this info isn't in the undo messages - presentation = event.at_xpath('presentation') - slide = event.at_xpath('pageNumber') - - presentation = determine_presentation(presentation, current_presentation) - slide = determine_slide_number(slide, current_slide) + presentation = determine_presentation(event.at_xpath('presentation'), current_presentation) + slide = determine_slide_number(event.at_xpath('pageNumber'), current_slide) # Newer undo messages have the shape id, making this a lot easier - shape_id = event.at_xpath('shapeId') - shape_id_nil = shape_id.nil? - shape_id = shape_id.text unless shape_id_nil + shape_id = event.at_xpath('shapeId')&.text # Set up the shapes data structures if needed shapes[presentation] ||= {} @@ -692,18 +679,17 @@ def events_parse_undo(shapes, event, current_presentation, current_slide, timest # We only need to deal with shapes for this slide shapes = shapes[presentation][slide] - if !shape_id_nil + if shape_id # If we have the shape id, we simply have to update the undo time on # all the shapes with that id. BigBlueButton.logger.info("Undo: removing shape with ID #{shape_id} at #{timestamp}") - set_undo_helper(shapes, :id, shape_id, timestamp) else # The undo command removes the most recently added shape that has not # already been removed by another undo or clear. Find that shape. - undo_pos = shapes.rindex { |s| s[:undo].nil? } - undo_shape = undo_pos.nil? ? nil : shapes[undo_pos] - if !undo_shape.nil? + undo_pos = shapes.rindex { |s| !s[:undo] } + undo_shape = undo_pos ? shapes[undo_pos] : nil + if undo_shape undo_shape_unique_id = undo_shape[:shape_unique_id] BigBlueButton.logger.info("Undo: removing Shape #{undo_shape_unique_id} at #{timestamp}") # We have an id number assigned to associate all the updated versions @@ -719,15 +705,12 @@ end def events_parse_clear(shapes, event, current_presentation, current_slide, timestamp) # Figure out what presentation+slide this clear is for, with fallbacks # for old BBB where this info isn't in the clear messages - presentation = event.at_xpath('presentation') - slide = event.at_xpath('pageNumber') - - presentation = determine_presentation(presentation, current_presentation) - slide = determine_slide_number(slide, current_slide) + presentation = determine_presentation(event.at_xpath('presentation'), current_presentation) + slide = determine_slide_number(event.at_xpath('pageNumber'), current_slide) # BigBlueButton 2.0 per-user clear features; default to full clear on older versions full_clear = event.at_xpath('fullClear') - full_clear = !full_clear.nil? ? (full_clear.text == 'true') : true + full_clear = full_clear ? (full_clear.text == 'true') : true user_id = event.at_xpath('userId')&.text # Set up the shapes data structures if needed @@ -740,7 +723,7 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times full_clear ? BigBlueButton.logger.info('Clear: removing all shapes') : BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") shapes.each do |shape| - if (full_clear || (user_id == shape[:user_id])) && ((shape_undo = shape[:undo]).nil? || (shape_undo > timestamp)) + if (full_clear || (user_id == shape[:user_id])) && (!(shape_undo = shape[:undo]) || (shape_undo > timestamp)) shape[:undo] = timestamp end end @@ -806,7 +789,6 @@ def process_presentation(package_dir) presenter = nil # Current deskshare state (affects presentation and pan/zoom) deskshare = false - slides = [] panzooms = [] cursors = [] @@ -876,9 +858,9 @@ def process_presentation(package_dir) cursor_visible = cursor_changed = true when 'WhiteboardCursorMoveEvent' - user_id = event.at_xpath('userId') + user_id = event.at_xpath('userId')&.text # Only draw cursor for current presentor. TODO multi-cursor support - if user_id.nil? || (user_id.text == presenter) + if (!user_id || user_id == presenter) cursor_x = event.at_xpath('xOffset').text.to_f cursor_y = event.at_xpath('yOffset').text.to_f cursor_visible = cursor_changed = true @@ -887,15 +869,14 @@ def process_presentation(package_dir) # Perform slide finalization if slide_changed slide = slides.last - slide_nil = slide.nil? - if !slide_nil && + if slide && (slide[:presentation] == current_presentation) && (slide[:slide] == current_slide) && (slide[:deskshare] == deskshare) BigBlueButton.logger.info('Presentation/Slide: skipping, no changes') else - unless slide_nil + if slide slide[:out] = timestamp svg_render_image(svg, slide, shapes) end @@ -916,10 +897,9 @@ def process_presentation(package_dir) if panzoom_changed slide = slides.last panzoom = panzooms.last - panzoom_nil = panzoom.nil? slide_width = slide[:width] slide_height = slide[:height] - if !panzoom_nil && + if panzoom && (panzoom[:x_offset] == current_x_offset) && (panzoom[:y_offset] == current_y_offset) && (panzoom[:width_ratio] == current_width_ratio) && @@ -930,7 +910,7 @@ def process_presentation(package_dir) BigBlueButton.logger.info('Panzoom: skipping, no changes') panzoom_changed = false else - unless panzoom_nil + if panzoom panzoom[:out] = timestamp panzooms_emit_event(panzooms_rec, panzoom) end @@ -959,14 +939,13 @@ def process_presentation(package_dir) panzoom = panzooms.last cursor = cursors.last - cursor_nil = cursor.nil? - if !cursor_nil && + if cursor && ((!cursor[:visible] && !cursor_visible) || ((cursor[:x] == cursor_x) && (cursor[:y] == cursor_y))) && !panzoom_changed BigBlueButton.logger.info('Cursor: skipping, no changes') else - unless cursor_nil + if cursor cursor[:out] = timestamp cursors_emit_event(cursors_rec, cursor) end @@ -1021,7 +1000,7 @@ def process_chat_messages(events, bbb_props) message: chat[:message], target: 'chat', } - unless (chat_out = chat[:out]).nil? + if (chat_out = chat[:out]) chattimeline[:out] = (chat_out / 1000.0).round(1) end @@ -1065,7 +1044,7 @@ end def get_poll_answers(event) answers = [] - unless (answers_event = event.at_xpath('answers')).nil? + if (answers_event = event.at_xpath('answers')) answers = JSON.parse(answers_event.content) end @@ -1141,7 +1120,7 @@ def process_external_video_events(_events, package_dir) start_timestamp = event[:start_timestamp] timestamp = (translate_timestamp(start_timestamp) / 1000).to_i # do not add same external_video twice - next unless external_videos.find { |ev| ev[:timestamp] == timestamp }.nil? + next if external_videos.find { |ev| ev[:timestamp] == timestamp } re_start_timestamp = re[:start_timestamp] re_stop_timestamp = re[:stop_timestamp] From e6476a9b725e2c38e808aabb5b70ba424441f15c Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Thu, 20 Jan 2022 13:57:53 +0100 Subject: [PATCH 21/26] Style changes; enable NewCops in Rubocop --- record-and-playback/.rubocop.yml | 1 + .../scripts/publish/presentation.rb | 146 ++++++++---------- 2 files changed, 69 insertions(+), 78 deletions(-) diff --git a/record-and-playback/.rubocop.yml b/record-and-playback/.rubocop.yml index 368e11ac5e..9f078d4a64 100644 --- a/record-and-playback/.rubocop.yml +++ b/record-and-playback/.rubocop.yml @@ -1,5 +1,6 @@ AllCops: TargetRubyVersion: 2.5 # System ruby on Ubuntu 18.04 + NewCops: enable Layout/HashAlignment: EnforcedHashRocketStyle: [ key, table ] EnforcedColonStyle: [ key, table ] diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 4b350afb6b..6ec61c8c4d 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -18,8 +18,6 @@ # with BigBlueButton; if not, see . # -_performance_start = Time.now - # For DEVELOPMENT # Allows us to run the script manually # require File.expand_path('../../../core/lib/recordandplayback', __dir__) @@ -40,13 +38,13 @@ bbb_props = BigBlueButton.read_props # There's a couple of places where stuff is mysteriously divided or multiplied # by 2. This is just here to call out how spooky that is. -@magic_mystery_number = 2 +MAGIC_MYSTERY_NUMBER = 2 def scale_to_deskshare_video(width, height) deskshare_video_height = deskshare_video_width = @presentation_props['deskshare_output_height'].to_f - scale = [deskshare_video_width / width, deskshare_video_height / height] - video_width = width * (scale_min = scale.min) + scale_min = [deskshare_video_width / width, deskshare_video_height / height].min + video_width = width * scale_min video_height = height * scale_min [video_width.floor, video_height.floor] @@ -107,7 +105,7 @@ end def color_to_hex(color) color = color.to_i.to_s(16) - '0' * (6 - color.length) + color + ('0' * (6 - color.length)) + color end def shape_scale_width(slide, x) @@ -119,11 +117,17 @@ def shape_scale_height(slide, y) end def shape_thickness(slide, shape) - (shape_thickness_percent = shape[:thickness_percent]) ? shape_scale_width(slide, shape_thickness_percent) : shape[:thickness] + shape_thickness_percent = shape[:thickness_percent] + if shape_thickness_percent + shape_scale_width(slide, shape[:thickness_percent]) + else + shape[:thickness] + end end def svg_render_shape_pencil(g, slide, shape) - g['shape'] = "pencil#{shape_unique_id = shape[:shape_unique_id]}" + shape_unique_id = shape[:shape_unique_id] + g['shape'] = "pencil#{shape_unique_id}" doc = g.document data_points = shape[:data_points] @@ -134,7 +138,6 @@ def svg_render_shape_pencil(g, slide, shape) end if data_points_length == 2 - # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing single point") g['style'] = "stroke:none;fill:##{shape[:color]};visibility:hidden" circle = doc.create_element('circle', cx: shape_scale_width(slide, data_points[0]), @@ -144,21 +147,18 @@ def svg_render_shape_pencil(g, slide, shape) else path = [] data_points = data_points.each - - if (shape_commands = shape[:commands]) - # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing from command string (#{shape_commands.length} commands)") + shape_commands = shape[:commands] + if shape_commands shape_commands.each do |command| case command - when 1, 2 # MOVE_TO, LINE_TO + when 1 # MOVE_TO x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) - path.push("#{command.eql?(1) ? 'M' : 'L'}#{x} #{y}") - when 3 # Q_CURVE_TO - cx1 = shape_scale_width(slide, data_points.next) - cy1 = shape_scale_height(slide, data_points.next) + path.push("M#{x} #{y}") + when 2 # LINE_TO x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) - path.push("Q#{cx1} #{cy1},#{x} #{y}") + path.push("L#{x} #{y}") when 4 # C_CURVE_TO cx1 = shape_scale_width(slide, data_points.next) cy1 = shape_scale_height(slide, data_points.next) @@ -186,10 +186,9 @@ def svg_render_shape_pencil(g, slide, shape) end end - path = path.join('') - g['style'] = - "stroke:##{shape[:color]};stroke-linecap:round;stroke-linejoin:round;stroke-width:#{shape_thickness(slide, - shape)};visibility:hidden;fill:none" + path = path.join + g['style'] = "stroke:##{shape[:color]};stroke-linecap:round;stroke-linejoin:round;" \ + "stroke-width:#{shape_thickness(slide, shape)};visibility:hidden;fill:none" svg_path = doc.create_element('path', d: path) g << svg_path end @@ -198,8 +197,8 @@ end def svg_render_shape_line(g, slide, shape) g['shape'] = "line#{shape[:shape_unique_id]}" g['style'] = - "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, - shape)};visibility:hidden;fill:none#{@version_atleast_2_0_0 ? ';stroke-linecap:butt' : ';stroke-linecap:round'}" + "stroke:##{shape[:color]};stroke-width:#{shape_thickness(slide, shape)};" \ + "visibility:hidden;fill:none;stroke-linecap:#{@version_atleast_2_0_0 ? 'butt' : 'round'}" doc = g.document data_points = shape[:data_points] @@ -212,14 +211,13 @@ def svg_render_shape_line(g, slide, shape) end def stroke_attributes(slide, shape) - "stroke:##{shape_color = shape[:color]};stroke-width:#{shape_thickness(slide, - shape)};visibility:hidden;fill:#{shape[:fill] ? "##{shape_color}" : 'none'}" + "stroke:##{shape_color = shape[:color]};stroke-width:#{shape_thickness(slide, shape)};" \ + "visibility:hidden;fill:#{shape[:fill] ? "##{shape_color}" : 'none'}" end def svg_render_shape_rect(g, slide, shape) g['shape'] = "rect#{shape[:shape_unique_id]}" - g['style'] = - "#{stroke_attributes(slide, shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter' : ';stroke-linejoin:round'}" + g['style'] = "#{stroke_attributes(slide, shape)};stroke-linejoin:#{@version_atleast_2_0_0 ? 'miter' : 'round'}" doc = g.document data_points = shape[:data_points] @@ -241,16 +239,14 @@ def svg_render_shape_rect(g, slide, shape) end end - path = doc.create_element('path', - d: "M#{x1} #{y1}L#{x2} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") + path = doc.create_element('path', d: "M#{x1} #{y1}L#{x2} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") g << path end def svg_render_shape_triangle(g, slide, shape) g['shape'] = "triangle#{shape[:shape_unique_id]}" - g['style'] = - "#{stroke_attributes(slide, - shape)}#{@version_atleast_2_0_0 ? ';stroke-linejoin:miter;stroke-miterlimit:8' : ';stroke-linejoin:round'}" + g['style'] = "#{stroke_attributes(slide, shape)};" \ + "stroke-linejoin:#{@version_atleast_2_0_0 ? 'miter;stroke-miterlimit:8' : 'round'}" doc = g.document data_points = shape[:data_points] @@ -261,8 +257,7 @@ def svg_render_shape_triangle(g, slide, shape) px = ((x1 + x2) / 2.0).round(5) - path = doc.create_element('path', - d: "M#{px} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") + path = doc.create_element('path', d: "M#{px} #{y1}L#{x2} #{y2}L#{x1} #{y2}Z") g << path end @@ -303,11 +298,11 @@ def svg_render_shape_ellipse(g, slide, shape) # path element's elliptical arc code renders r_x or r_y # degenerate cases as line segments, so we can use that. path = "M#{x1} #{hy}" \ - "A#{width_r} #{height_r} 0 0 1 #{hx} #{y1}" \ - "A#{width_r} #{height_r} 0 0 1 #{x2} #{hy}" \ - "A#{width_r} #{height_r} 0 0 1 #{hx} #{y2}" \ - "A#{width_r} #{height_r} 0 0 1 #{x1} #{hy}" \ - 'Z' + "A#{width_r} #{height_r} 0 0 1 #{hx} #{y1}" \ + "A#{width_r} #{height_r} 0 0 1 #{x2} #{hy}" \ + "A#{width_r} #{height_r} 0 0 1 #{hx} #{y2}" \ + "A#{width_r} #{height_r} 0 0 1 #{x1} #{hy}" \ + 'Z' svg_path = doc.create_element('path', d: path) g << svg_path @@ -324,7 +319,6 @@ def svg_render_shape_text(g, slide, shape) height = shape_scale_height(slide, shape[:text_box_height]) font_size = shape_scale_height(slide, shape[:calced_font_size]) - # BigBlueButton.logger.info("Text #{shape[:shape_unique_id]} width #{width} height #{height} font size #{font_size}") g['style'] = "color:##{shape[:font_color]};word-wrap:break-word;visibility:hidden;font-family:Arial;font-size:#{font_size}px" switch = doc.create_element('switch') @@ -361,10 +355,10 @@ def svg_render_shape_poll(g, slide, shape) svg_file = "#{@process_dir}/presentation/#{presentation}/poll_result#{poll_id}.svg" # Save the poll json to a temp file - IO.write(json_file, result) + File.open(json_file, 'w') { |f| f.write result } # Render the poll svg - ret = BigBlueButton.exec_ret('utils/gen_poll_svg', '-i', json_file, '-w', width.round.to_s, '-h', height.round.to_s, '-n', - num_responders.to_s, '-o', svg_file) + ret = BigBlueButton.exec_ret('utils/gen_poll_svg', '-i', json_file, '-w', width.round.to_s, '-h', height.round.to_s, + '-n', num_responders.to_s, '-o', svg_file) raise 'Failed to generate poll svg' if ret != 0 # Poll image @@ -374,7 +368,10 @@ def svg_render_shape_poll(g, slide, shape) end def svg_render_shape(canvas, slide, shape, image_id) - if (shape_in = shape[:in]) == (shape_out = shape[:out]) + shape_in = shape[:in] + shape_out = shape[:out] + + if shape_in == shape_out BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is never shown (duration rounds to 0)") return end @@ -384,8 +381,6 @@ def svg_render_shape(canvas, slide, shape, image_id) return end - # BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} Type #{shape[:type]} from #{shape_in} to #{shape_out} undo #{shape[:undo]}") - doc = canvas.document g = doc.create_element('g', id: "image#{image_id}-draw#{shape[:shape_id]}", class: 'shape', @@ -419,8 +414,10 @@ end def svg_render_image(svg, slide, shapes) slide_number = slide[:slide] presentation = slide[:presentation] + slide_in = slide[:in] + slide_out = slide[:out] - if (slide_in = slide[:in]) == (slide_out = slide[:out]) + if slide_in == slide_out BigBlueButton.logger.info("Presentation #{presentation} Slide #{slide_number} is never shown (duration rounds to 0)") return end @@ -438,9 +435,9 @@ def svg_render_image(svg, slide, shapes) 'xlink:href' => slide[:src], width: slide[:width], height: slide[:height], x: 0, y: 0, style: 'visibility:hidden') - if (slide_text = slide[:text]) - image['text'] = slide_text - end + + slide_text = slide[:text] + image['text'] = slide_text if slide_text svg << image return if slide_deskshare || !shapes.dig(presentation, slide_number) @@ -464,8 +461,8 @@ def panzoom_viewbox(panzoom) panzoom[:width_ratio] = panzoom[:height_ratio] = 100.0 end - x = (-panzoom[:x_offset] * @magic_mystery_number / 100.0 * panzoom[:width]).round(5) - y = (-panzoom[:y_offset] * @magic_mystery_number / 100.0 * panzoom[:height]).round(5) + x = (-panzoom[:x_offset] * MAGIC_MYSTERY_NUMBER / 100.0 * panzoom[:width]).round(5) + y = (-panzoom[:y_offset] * MAGIC_MYSTERY_NUMBER / 100.0 * panzoom[:height]).round(5) w = shape_scale_width(panzoom, panzoom[:width_ratio]) h = shape_scale_height(panzoom, panzoom[:height_ratio]) @@ -473,10 +470,8 @@ def panzoom_viewbox(panzoom) end def panzooms_emit_event(rec, panzoom) - if (panzoom_in = panzoom[:in]) == panzoom[:out] - BigBlueButton.logger.info('Panzoom: not emitting, duration rounds to 0') - return - end + panzoom_in = panzoom[:in] + return if panzoom_in == panzoom[:out] rec.event(timestamp: panzoom_in) do x, y, w, h = panzoom_viewbox(panzoom) @@ -486,14 +481,12 @@ def panzooms_emit_event(rec, panzoom) end def convert_cursor_coordinate(cursor_coord, panzoom_offset, panzoom_ratio) - (((cursor_coord / 100.0) + (panzoom_offset * @magic_mystery_number / 100.0)) / (panzoom_ratio / 100.0)).round(5) + (((cursor_coord / 100.0) + (panzoom_offset * MAGIC_MYSTERY_NUMBER / 100.0)) / (panzoom_ratio / 100.0)).round(5) end def cursors_emit_event(rec, cursor) - if (cursor_in = cursor[:in]) == cursor[:out] - # BigBlueButton.logger.info('Cursor: not emitting, duration rounds to 0') - return - end + cursor_in = cursor[:in] + return if cursor_in == cursor[:out] rec.event(timestamp: cursor_in) do panzoom = cursor[:panzoom] @@ -515,8 +508,6 @@ def cursors_emit_event(rec, cursor) rec.cursor("#{x} #{y}") end - - # BigBlueButton.logger.info("Cursor #{cursor_e.content} at #{cursor[:in]}") end @svg_shape_id = 1 @@ -527,7 +518,7 @@ def determine_presentation(presentation, current_presentation) end def determine_slide_number(slide, current_slide) - return current_slide if !slide + return current_slide unless slide slide = slide.text.to_i slide -= 1 unless @version_atleast_0_9_0 @@ -561,9 +552,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times user_id = event.at_xpath('userId')&.text shape[:user_id] = user_id if user_id - if (shape_id = event.at_xpath('id')&.text) - shape[:id] = shape_id - end + shape_id = event.at_xpath('id')&.text + shape[:id] = shape_id if shape_id status = event.at_xpath('status')&.text shape_status = shape[:status] = status if status @@ -860,7 +850,7 @@ def process_presentation(package_dir) when 'WhiteboardCursorMoveEvent' user_id = event.at_xpath('userId')&.text # Only draw cursor for current presentor. TODO multi-cursor support - if (!user_id || user_id == presenter) + if !user_id || user_id == presenter cursor_x = event.at_xpath('xOffset').text.to_f cursor_y = event.at_xpath('yOffset').text.to_f cursor_visible = cursor_changed = true @@ -1191,7 +1181,9 @@ begin target_dir = "#{@recording_dir}/publish/presentation/#{@meeting_id}" @deskshare_dir = "#{@recording_dir}/raw/#{@meeting_id}/deskshare" - if !FileTest.directory?(target_dir) + if FileTest.directory?(target_dir) + BigBlueButton.logger.info("#{target_dir} is already there") + else BigBlueButton.logger.info('Making dir target_dir') FileUtils.mkdir_p target_dir @@ -1203,17 +1195,17 @@ begin video_formats = @presentation_props['video_formats'] video_files = Dir.glob("#{@process_dir}/webcams.{#{video_formats.join(',')}}") - if !video_files.empty? - copy_media_files_helper('video', video_files, package_dir) - else + if video_files.empty? copy_media_files_helper('audio', ["#{@process_dir}/audio.webm", "#{@process_dir}/audio.ogg"], package_dir) + else + copy_media_files_helper('video', video_files, package_dir) end video_files = Dir.glob("#{@process_dir}/deskshare.{#{video_formats.join(',')}}") - if !video_files.empty? - copy_media_files_helper('deskshare', video_files, package_dir) - else + if video_files.empty? BigBlueButton.logger.info("Could not copy deskshares.webm: file doesn't exist") + else + copy_media_files_helper('deskshare', video_files, package_dir) end if File.exist?("#{@process_dir}/captions.json") @@ -1347,8 +1339,6 @@ begin exit 1 end generate_done_or_fail_file(true) - else - BigBlueButton.logger.info("#{target_dir} is already there") end end rescue StandardError => e From b5a52b8e893c4825f9e7cc1ea7daf0ea72db7bde Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 31 Jan 2022 16:26:22 +0100 Subject: [PATCH 22/26] Second round of stylistic changes; search for previous shape in DRAW_END --- .../scripts/publish/presentation.rb | 116 ++++++++++-------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 6ec61c8c4d..5661cc3efa 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -336,6 +336,12 @@ def svg_render_shape_text(g, slide, shape) end def svg_render_shape_poll(g, slide, shape) + result = shape[:result] + if result == "[]" + BigBlueButton.logger.info("Poll #{shape[:shape_unique_id]} result is empty (no options/answers), ignoring...") + return + end + poll_id = shape[:shape_unique_id] g['shape'] = "poll#{poll_id}" g['style'] = 'visibility:hidden' @@ -347,7 +353,6 @@ def svg_render_shape_poll(g, slide, shape) width = shape_scale_width(slide, data_points[2]) height = shape_scale_height(slide, data_points[3]) - result = shape[:result] num_responders = shape[:num_responders] presentation = slide[:presentation] @@ -376,7 +381,7 @@ def svg_render_shape(canvas, slide, shape, image_id) return end - if (shape_in >= slide[:out]) || (shape_out && (shape_out <= slide[:in])) + if (shape_in >= slide[:out]) || (!shape[:out].nil? && shape[:out] <= slide[:in]) BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is not visible during image time span") return end @@ -384,7 +389,7 @@ def svg_render_shape(canvas, slide, shape, image_id) doc = canvas.document g = doc.create_element('g', id: "image#{image_id}-draw#{shape[:shape_id]}", class: 'shape', - timestamp: shape_in, undo: shape[:undo] || -1) + timestamp: shape_in, undo: (shape[:undo].nil? ? -1 : shape[:undo])) case shape[:type] when 'pencil' @@ -436,8 +441,7 @@ def svg_render_image(svg, slide, shapes) width: slide[:width], height: slide[:height], x: 0, y: 0, style: 'visibility:hidden') - slide_text = slide[:text] - image['text'] = slide_text if slide_text + image['text'] = slide[:text] if slide[:text] svg << image return if slide_deskshare || !shapes.dig(presentation, slide_number) @@ -496,7 +500,7 @@ def cursors_emit_event(rec, cursor) # Use the panzoom information to convert it to be relative to viewbox x = convert_cursor_coordinate(cursor[:x], panzoom[:x_offset], panzoom[:width_ratio]) y = convert_cursor_coordinate(cursor[:y], panzoom[:y_offset], panzoom[:height_ratio]) - x = y = -1.0 if x.negative? || (x > 1) || y.negative? || (y > 1) + x = y = -1.0 if (x < 0) || (x > 1) || (y < 0) || (y > 1) else # Cursor position is relative to the visible area x = cursor[:x].round(5) @@ -510,9 +514,6 @@ def cursors_emit_event(rec, cursor) end end -@svg_shape_id = 1 -@svg_shape_unique_id = 1 - def determine_presentation(presentation, current_presentation) presentation&.text || current_presentation end @@ -563,7 +564,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times # Some shape-specific properties if %w[ellipse line pencil rectangle triangle].include?(shape_type) shape[:color] = color_to_hex(event.at_xpath('color').text) - unless (thickness = event.at_xpath('thickness')) + thickness = event.at_xpath('thickness') + unless thickness BigBlueButton.logger.warn("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} is missing thickness") return end @@ -596,7 +598,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times shape[:text_box_width] = event.at_xpath('textBoxWidth').text.to_f shape[:text_box_height] = event.at_xpath('textBoxHeight').text.to_f - unless (calced_font_size = event.at_xpath('calcedFontSize')) + calced_font_size = event.at_xpath('calcedFontSize') + unless calced_font_size BigBlueButton.logger.warn("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} is missing calcedFontSize") return end @@ -612,10 +615,8 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times if shape_id # If we have a shape ID, look up the previous shape by ID # Don't look for updates if the drawing has ended - unless shape_status == 'DRAW_END' - prev_shape_pos = shapes.rindex { |s| s[:id] == shape_id } - prev_shape = prev_shape_pos ? shapes[prev_shape_pos] : nil - end + prev_shape_pos = shapes.rindex { |s| s[:id] == shape_id } + prev_shape = prev_shape_pos ? shapes[prev_shape_pos] : nil else # No shape ID, so do heuristic matching. If the previous shape had the # same type and same first two data points, update it. @@ -641,7 +642,6 @@ def events_parse_shape(shapes, event, current_presentation, current_slide, times @svg_shape_unique_id += 1 end - # BigBlueButton.logger.info("Draw #{draw_id} Shape #{shape[:shape_unique_id]} ID #{shape_id} Type #{shape[:type]}") shapes << shape end @@ -649,7 +649,7 @@ def set_undo_helper(shapes, key, id, timestamp) shapes.each do |shape| next unless shape[key] == id - shape[:undo] = timestamp if !(shape_undo = shape[:undo]) || (shape_undo > timestamp) + shape[:undo] = timestamp if !shape[:undo] || (shape[:undo] > timestamp) end end @@ -710,22 +710,32 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times # We only need to deal with shapes for this slide shapes = shapes[presentation][slide] - full_clear ? BigBlueButton.logger.info('Clear: removing all shapes') : BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") + if full_clear + BigBlueButton.logger.info("Clear: removing all shapes") + else + BigBlueButton.logger.info("Clear: removing shapes for User #{user_id}") + end shapes.each do |shape| - if (full_clear || (user_id == shape[:user_id])) && (!(shape_undo = shape[:undo]) || (shape_undo > timestamp)) - shape[:undo] = timestamp + if full_clear || user_id == shape[:user_id] + if !shape[:undo] || shape[:undo] > timestamp + shape[:undo] = timestamp + end end end end def events_get_image_info(slide) - if (slide_deskshare = slide[:deskshare]) + slide_deskshare = slide[:deskshare] + slide_presentation = slide[:presentation] + + if slide_deskshare slide[:src] = 'presentation/deskshare.png' - elsif (slide_presentation = slide[:presentation]) == '' + elsif slide_presentation == '' slide[:src] = 'presentation/logo.png' else - slide[:src] = "presentation/#{slide_presentation}/slide-#{slide_nr = slide[:slide] + 1}.png" + slide_nr = slide[:slide] + 1 + slide[:src] = "presentation/#{slide_presentation}/slide-#{slide_nr}.png" slide[:text] = "presentation/#{slide_presentation}/textfiles/slide-#{slide_nr}.txt" end image_path = "#{@process_dir}/#{slide[:src]}" @@ -920,34 +930,33 @@ def process_presentation(package_dir) end # Perform cursor finalization - next unless cursor_changed || panzoom_changed - - unless (cursor_x >= 0) && (cursor_x <= 100) && - cursor_y >= 0 && (cursor_y <= 100) - cursor_visible = false - end - - panzoom = panzooms.last - cursor = cursors.last - if cursor && - ((!cursor[:visible] && !cursor_visible) || - ((cursor[:x] == cursor_x) && (cursor[:y] == cursor_y))) && - !panzoom_changed - BigBlueButton.logger.info('Cursor: skipping, no changes') - else - if cursor - cursor[:out] = timestamp - cursors_emit_event(cursors_rec, cursor) + if cursor_changed || panzoom_changed + unless cursor_x >= 0 && cursor_x <= 100 && + cursor_y >= 0 && cursor_y <= 100 + cursor_visible = false + end + + panzoom = panzooms.last + cursor = cursors.last + if cursor && + ((!cursor[:visible] && !cursor_visible) || + (cursor[:x] == cursor_x && cursor[:y] == cursor_y)) && + !panzoom_changed + BigBlueButton.logger.info('Cursor: skipping, no changes') + else + if cursor + cursor[:out] = timestamp + cursors_emit_event(cursors_rec, cursor) + end + cursor = { + visible: cursor_visible, + x: cursor_x, + y: cursor_y, + panzoom: panzoom, + in: timestamp, + } + cursors << cursor end - # BigBlueButton.logger.info("Cursor: visible #{cursor_visible}, #{cursor_x} #{cursor_y} (#{panzoom[:width]}x#{panzoom[:height]})") - cursor = { - visible: cursor_visible, - x: cursor_x, - y: cursor_y, - panzoom: panzoom, - in: timestamp, - } - cursors << cursor end end @@ -1079,7 +1088,8 @@ def process_poll_events(events, package_dir) published_polls = [] @rec_events.each do |re| events.xpath("recording/event[@eventname='PollPublishedRecordEvent']").each do |event| - next unless ((timestamp = event[:timestamp]).to_i >= re[:start_timestamp]) && (timestamp.to_i <= re[:stop_timestamp]) + timestamp = event[:timestamp] + next unless (timestamp.to_i >= re[:start_timestamp]) && (timestamp.to_i <= re[:stop_timestamp]) published_polls << { timestamp: (translate_timestamp(timestamp) / 1000).to_i, @@ -1106,7 +1116,7 @@ def process_external_video_events(_events, package_dir) external_videos = [] @rec_events.each do |re| external_video_events.each do |event| - # BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}") + BigBlueButton.logger.info("Processing rec event #{re} and external video event #{event}") start_timestamp = event[:start_timestamp] timestamp = (translate_timestamp(start_timestamp) / 1000).to_i # do not add same external_video twice @@ -1148,6 +1158,8 @@ end @panzooms_xml_filename = 'panzooms.xml' @cursor_xml_filename = 'cursor.xml' @deskshare_xml_filename = 'deskshare.xml' +@svg_shape_id = 1 +@svg_shape_unique_id = 1 opts = Optimist.options do opt :meeting_id, 'Meeting id to archive', default: '58f4a6b3-cd07-444d-8564-59116cb53974', type: String From a280959f91e5ee13749bc8864cd22825282e6662 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Mon, 31 Jan 2022 17:09:15 +0100 Subject: [PATCH 23/26] Remove comment --- record-and-playback/presentation/scripts/publish/presentation.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 782d992cb1..afc704c061 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -479,7 +479,6 @@ def panzooms_emit_event(rec, panzoom) rec.event(timestamp: panzoom_in) do x, y, w, h = panzoom_viewbox(panzoom) rec.viewBox("#{x} #{y} #{w} #{h}") - # BigBlueButton.logger.info("Panzoom viewbox #{x} #{y} #{w} #{h}" at #{panzoom[:in]}") end end From d11f9e22a0629b49b744a3458fc769ad0e6c8449 Mon Sep 17 00:00:00 2001 From: Sebastian Berm <6470150+sebastianberm@users.noreply.github.com> Date: Fri, 18 Feb 2022 14:58:17 +0100 Subject: [PATCH 24/26] fix: Fixing typo bbb-conf (#14398) Not really something that fixes any functions, but it does fix a typo, which has been bothering me for a while. --- bigbluebutton-config/bin/apply-lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bigbluebutton-config/bin/apply-lib.sh b/bigbluebutton-config/bin/apply-lib.sh index 825dfd9c9c..0684009f48 100644 --- a/bigbluebutton-config/bin/apply-lib.sh +++ b/bigbluebutton-config/bin/apply-lib.sh @@ -105,7 +105,7 @@ enableUFWRules() { enableMultipleKurentos() { - echo " - Configuring three Kurento Media Servers (listen only, webcam, and screeshare)" + echo " - Configuring three Kurento Media Servers (listen only, webcam, and screenshare)" # Step 1. Setup shared certificate between FreeSWITCH and Kurento From f9b022c7e4d1ae530dd51d6a06fcc9f4f8754c81 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Tue, 22 Mar 2022 18:02:07 +0100 Subject: [PATCH 25/26] Don't show slides after meeting ends --- .../presentation/scripts/publish/presentation.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index 6d8d0cca8c..d1f1f0c3d0 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -178,7 +178,6 @@ def svg_render_shape_pencil(g, slide, shape) end end else - # BigBlueButton.logger.info("Pencil #{shape_unique_id}: Drawing simple line (#{data_points_length / 2} points)") x = shape_scale_width(slide, data_points.next) y = shape_scale_height(slide, data_points.next) path << "M#{x} #{y}" @@ -427,7 +426,7 @@ def svg_render_image(svg, slide, shapes) slide_in = slide[:in] slide_out = slide[:out] - if slide_in == slide_out + if slide_in == slide_out || slide_in > (@recording_time / 1000) BigBlueButton.logger.info("Presentation #{presentation} Slide #{slide_number} is never shown (duration rounds to 0)") return end @@ -1254,8 +1253,7 @@ begin BigBlueButton::Events.get_start_and_stop_rec_events(@doc) ) - recording_time = BigBlueButton::Events.get_recording_length(@doc) - + @recording_time = BigBlueButton::Events.get_recording_length(@doc) @meeting_start = BigBlueButton::Events.first_event_timestamp(@doc) @meeting_end = BigBlueButton::Events.last_event_timestamp(@doc) @@ -1290,7 +1288,7 @@ begin xml.format('presentation') xml.link("#{playback_protocol}://#{playback_host}/playback/presentation/2.3/#{@meeting_id}") xml.processing_time(processing_time.to_s) - xml.duration(recording_time.to_s) + xml.duration(@recording_time.to_s) unless presentation.empty? xml.extensions do xml.preview do From 6f4a3fdf0b53915ff6d012a1cd99e0585efa4566 Mon Sep 17 00:00:00 2001 From: Calvin Walton Date: Wed, 23 Mar 2022 09:55:42 -0400 Subject: [PATCH 26/26] publish/presentation.rb: clean up some assignments in if statements --- .../presentation/scripts/publish/presentation.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb index d1f1f0c3d0..68ecaeeae8 100755 --- a/record-and-playback/presentation/scripts/publish/presentation.rb +++ b/record-and-playback/presentation/scripts/publish/presentation.rb @@ -1236,13 +1236,11 @@ begin end end - if File.exist?(presentation_text = "#{@process_dir}/presentation_text.json") - FileUtils.cp(presentation_text, package_dir) - end + presentation_text = "#{@process_dir}/presentation_text.json" + FileUtils.cp(presentation_text, package_dir) if File.exist?(presentation_text) - if File.exist?(notes = "#{@process_dir}/notes/notes.html") - FileUtils.cp(notes, package_dir) - end + notes = "#{@process_dir}/notes/notes.html" + FileUtils.cp(notes, package_dir) if File.exist?(notes) processing_time = File.read("#{@process_dir}/processing_time")