# Set encoding to utf-8
# encoding: UTF-8
#
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see .
#
performance_start = Time.now
require '../../core/lib/recordandplayback'
require 'rubygems'
require 'trollop'
require 'yaml'
require 'builder'
require 'fastimage' # require fastimage to get the image size of the slides (gem install fastimage)
# used to convert the colours to hex
class String
def convert_base(from, to)
self.to_i(from).to_s(to)
end
end
def processPanAndZooms
#Create panzooms.xml
BigBlueButton.logger.info("Creating panzooms.xml")
$panzooms_xml = Nokogiri::XML::Builder.new do |xml|
$xml = xml
$xml.recording('id' => 'panzoom_events') do
h_ratio_prev = nil
w_ratio_prev = nil
x_prev = nil
y_prev = nil
timestamp_orig_prev = nil
timestamp_prev = nil
last_time = $panzoom_events.last[:timestamp].to_f
$panzoom_events.each do |panZoomEvent|
# Get variables
timestamp_orig = panZoomEvent[:timestamp].to_f
timestamp = ((timestamp_orig-$join_time)/1000).round(1)
h_ratio = panZoomEvent.xpath(".//heightRatio")[0].text()
w_ratio = panZoomEvent.xpath(".//widthRatio")[0].text()
x = panZoomEvent.xpath(".//xOffset")[0].text()
y = panZoomEvent.xpath(".//yOffset")[0].text()
if(timestamp_prev == timestamp)
if(timestamp_orig == last_time)
if(h_ratio && w_ratio && x && y)
$xml.event(:timestamp => timestamp, :orig => timestamp_orig) do
$ss.each do |key,val|
$val = val
if key === timestamp
$vbox_width = $val[0]
$vbox_height = $val[1]
end
end
$xml.viewBox "#{($vbox_width-((1-((x.to_f.abs)*$magic_mystery_number/100.0))*$vbox_width))} #{($vbox_height-((1-((y.to_f.abs)*$magic_mystery_number/100.0))*$vbox_height)).round(2)} #{((w_ratio.to_f/100.0)*$vbox_width).round(1)} #{((h_ratio.to_f/100.0)*$vbox_height).round(1)}"
end
end
end
# do nothing because playback can't react that fast
else
if(h_ratio_prev && w_ratio_prev && x_prev && y_prev)
$xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
$ss.each do |key,val|
$val = val
if key === timestamp_prev
$vbox_width = $val[0]
$vbox_height = $val[1]
end
end
$xml.viewBox "#{($vbox_width-((1-((x_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_width))} #{($vbox_height-((1-((y_prev.to_f.abs)*$magic_mystery_number/100.0))*$vbox_height)).round(2)} #{((w_ratio_prev.to_f/100.0)*$vbox_width).round(1)} #{((h_ratio_prev.to_f/100.0)*$vbox_height).round(1)}"
end
end
end
timestamp_prev = timestamp
timestamp_orig_prev = timestamp_orig
h_ratio_prev = h_ratio
w_ratio_prev = w_ratio
x_prev = x
y_prev = y
end
end
end
BigBlueButton.logger.info("Finished creating panzooms.xml")
end
def processCursorEvents
BigBlueButton.logger.info("Processing cursor events")
$cursor_xml = Nokogiri::XML::Builder.new do |xml|
$xml = xml
$xml.recording('id' => 'cursor_events') do
x_prev = nil
y_prev = nil
timestamp_orig_prev = nil
timestamp_prev = nil
if(!$cursor_events.empty?)
last_time = $cursor_events.last[:timestamp].to_f
$cursor_events.each do |cursorEvent|
timestamp_orig = cursorEvent[:timestamp].to_f
timestamp = ((timestamp_orig-$join_time)/1000).round(1)
x = cursorEvent.xpath(".//xOffset")[0].text()
y = cursorEvent.xpath(".//yOffset")[0].text()
if(timestamp_prev == timestamp)
else
if(x_prev && y_prev)
$xml.event(:timestamp => timestamp_prev, :orig => timestamp_orig_prev) do
$ss.each do |key,val|
$val = val
if key === timestamp_prev
$vbox_width = $val[0]/2 # because the image size is twice as big as the viewbox
$vbox_height = $val[1]/2 # because the image size is twice as big as the viewbox
end
end
$xml.cursor "#{($vbox_width.to_f*x.to_f).round(1)} #{($vbox_height.to_f*y.to_f).round(1)}"
end
end
end
timestamp_prev = timestamp
timestamp_orig_prev = timestamp_orig
x_prev = x
y_prev = y
end
end
end
end
BigBlueButton.logger.info("Finished processing cursor events")
end
def processClearEvents
# process all the cleared pages events.
$clear_page_events.each do |clearEvent|
clearTime = ((clearEvent[:timestamp].to_f - $join_time)/1000).round(1)
$pageCleared = clearEvent.xpath(".//pageNumber")[0].text()
slideFolder = clearEvent.xpath(".//presentation")[0].text()
#$clearPageTimes[clearTime] = [$pageCleared, $canvas_number, "presentation/#{slideFolder}/slide-#{$pageCleared.to_i+1}.png", nil]
$clearPageTimes[($prev_clear_time..clearTime)] = [$pageCleared, $canvas_number, "presentation/#{slideFolder}/slide-#{$pageCleared.to_i+1}.png", nil]
$prev_clear_time = clearTime
$canvas_number+=1
end
end
def processUndoEvents
# Processing the undo events, creating/filling a hashmap called "undos".
BigBlueButton.logger.info("Process undo events.")
$undo_events.each do |undo|
closest_shape = nil # Initialize as nil to prime the loop.
t = undo[:timestamp].to_f
$shape_events.each do |shape|
# The undo cannot be for a shape that hasn't been drawn yet.
if shape[:timestamp].to_f < t
# It must be the closest shape drawn that hasn't already been undone.
if (closest_shape == nil) || (shape[:timestamp].to_f > closest_shape[:timestamp].to_f)
# It cannot be an undo for another shape already.
if !($undos.has_key? shape)
# Must be part of this presentation of course
if shape.xpath(".//pageNumber")[0].text() == undo.xpath(".//pageNumber")[0].text()
# Must be a shape in this page too.
if shape.xpath(".//presentation")[0].text() == undo.xpath(".//presentation")[0].text()
if ((shape.xpath(".//type")[0].text() == "rectangle") || (shape.xpath(".//type")[0].text() == "ellipse"))
shape_already_processed = false
if($undos.length == 0)
shape_already_processed = false
else
$undos.each do |u, v|
if shape.xpath(".//dataPoints")[0].text().split(",")[0] == u.xpath(".//dataPoints")[0].text().split(",")[0]
if shape.xpath(".//dataPoints")[0].text().split(",")[1] == u.xpath(".//dataPoints")[0].text().split(",")[1]
shape_already_processed = true
end
end
end
end
if !(shape_already_processed)
closest_shape = shape
end
else
closest_shape = shape
end
end
end
end
end
end
end
if(closest_shape != nil)
$undos[closest_shape] = ((undo[:timestamp].to_f - $join_time)/1000).round(1)
end
end
$undos_temp = {}
$undos.each do |un, val|
$undos_temp[((un[:timestamp].to_f - $join_time)/1000).round(1)] = val
end
$undos = $undos_temp
BigBlueButton.logger.info("Undos: #{$undos}")
end
def processClearImages
BigBlueButton.logger.info("Put image numbers in clearPageTimes")
$slides_compiled.each do |key, val|
$clearPageTimes.each do |cpt, pgCanvasUrl|
# check if the src of the slide matches the url of the clear event
if key[0] == pgCanvasUrl[2]
# put the image number into the $clearPageTimes
pgCanvasUrl[3] = "image#{val[2].to_i}"
end
end
end
end
def storePencilShape
$line_count = $line_count + 1 # always update the line count!
$xml.g(:class => :shape, :id=>"draw#{$shapeCreationTime}", :undo => $shapeUndoTime, :shape =>"line#{$line_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; stroke-linecap: round; ") do
for i in (0...($shapeDataPoints.length/2)-1) do
$xml.line(:x1 => (($shapeDataPoints[i*2].to_f)/100)*$vbox_width, :y1 => (($shapeDataPoints[(i*2)+1].to_f)/100)*$vbox_height, :x2 => (($shapeDataPoints[(i*2)+2].to_f)/100)*$vbox_width, :y2 => (($shapeDataPoints[(i*2)+3].to_f)/100)*$vbox_height)
end
end
end
def storeRectShape
if($shapeCreationTime != $prev_time)
if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
# do not update the rectangle count
else
$rectangle_count = $rectangle_count + 1
end
$xml.g(:class => :shape, :id => "draw#{$shapeCreationTime}", :undo => $shapeUndoTime, :shape => "rect#{$rectangle_count}", :style => "stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
$originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
$originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
$originalOriginX = $originX
$originalOriginY = $originY
rectWidth = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
rectHeight = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
# Cannot have a negative height or width so we adjust
if(rectHeight < 0)
$originY = $originY + rectHeight
rectHeight = rectHeight.abs
end
if(rectWidth < 0)
$originX = $originX + rectWidth
rectWidth = rectWidth.abs
end
$xml.rect(:x => $originX, :y => $originY, :width => rectWidth, :height => rectHeight)
$prev_time = $shapeCreationTime
end
end
end
def storeEllipseShape
if($shapeCreationTime != $prev_time)
if(($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
# do not update the rectangle count
else
$ellipse_count = $ellipse_count + 1
end # end (($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
$xml.g(:class => :shape, :id => "draw#{$shapeCreationTime}", :undo => $shapeUndoTime, :shape => "ellipse#{$ellipse_count}", :style =>"stroke:\##{$colour_hex}; stroke-width:#{$shapeThickness}; visibility:hidden; fill:none") do
$originX = (($shapeDataPoints[0].to_f)/100)*$vbox_width
$originY = (($shapeDataPoints[1].to_f)/100)*$vbox_height
$originalOriginX = $originX
$originalOriginY = $originY
ellipseWidth = (($shapeDataPoints[2].to_f - $shapeDataPoints[0].to_f)/100)*$vbox_width
ellipseHeight = (($shapeDataPoints[3].to_f - $shapeDataPoints[1].to_f)/100)*$vbox_height
if(ellipseHeight < 0)
$originY = $originY + ellipseHeight
ellipseHeight = ellipseHeight.abs
end
if(ellipseWidth < 0)
$originX = $originX + ellipseWidth
ellipseWidth = ellipseWidth.abs
end
$xml.ellipse(:cx => $originX+(ellipseWidth/2), :cy => $originY+(ellipseHeight/2), :rx => ellipseWidth/2, :ry => ellipseHeight/2)
$prev_time = $shapeCreationTime
end # end xml.g
end # end if($shapeCreationTime != $prev_time)
end
def storeTextShape
if($shapeCreationTime != $prev_time)
$xml.g(:class => :shape, :id => "draw#{$shapeCreationTime}", :undo => $shapeUndoTime, :shape => "text#{$text_count}", :style => "fill:\##{$colour_hex}; visibility:hidden; font-family: #{$textFontType}; font-size: #{$textFontSize};") do
$xml.text_(:x => "#{(($shapeDataPoints[0].to_f)/100)*$vbox_width}", :y => "#{(($shapeDataPoints[1].to_f)/100)*$vbox_height}") do
$xml.text($textValue)
end
$prev_time = $shapeCreationTime
end # end xml.g
end # end if($shapeCreationTime != $prev_time)
end
def processSlideEvents
BigBlueButton.logger.info("Slide events processing")
# For each slide (there is only one image per slide)
$slides_events.each do |node|
eventname = node['eventname']
if eventname == "SharePresentationEvent"
$presentation_name = node.xpath(".//presentationName")[0].text()
else
slide_timestamp = node[:timestamp]
slide_start = ((slide_timestamp.to_f - $meeting_start.to_f) / 1000).round(1)
slide_number = node.xpath(".//slide")[0].text()
slide_src = "presentation/#{$presentation_name}/slide-#{slide_number.to_i + 1}.png"
slide_text = "presentation/#{$presentation_name}/textfiles/slide-#{slide_number.to_i + 1}.txt"
image_url = "#{$process_dir}/#{slide_src}"
slide_size = FastImage.size(image_url)
current_index = $slides_events.index(node)
if(current_index + 1 < $slides_events.length)
slide_end = (( $slides_events[current_index + 1][:timestamp].to_f - $meeting_start.to_f ) / 1000).round(1)
else
slide_end = (( $meeting_end.to_f - $meeting_start.to_f ) / 1000).round(1)
end
BigBlueButton.logger.info("Processing slide image")
# Is this a new image or one previously viewed?
if($slides_compiled[[slide_src, slide_size[1], slide_size[0]]] == nil)
# If it is, add it to the list with all the data.
$slides_compiled[[slide_src, slide_size[1], slide_size[0]]] = [[slide_start], [slide_end], $global_slide_count, slide_text]
$global_slide_count = $global_slide_count + 1
elsif
# If not, append new in and out times to the old entry
$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][0] << slide_start
$slides_compiled[[slide_src, slide_size[1], slide_size[0]]][1] << slide_end
end
$ss[(slide_start..slide_end)] = slide_size # store the size of the slide at that range of time
puts "#{slide_src} : #{slide_start} -> #{slide_end}"
end
end
end
def processShapesAndClears
# Create shapes.svg file from the events.xml
BigBlueButton.logger.info("Creating shapes.svg")
$shapes_svg = Nokogiri::XML::Builder.new do |xml|
$xml = xml
processClearEvents()
processUndoEvents()
# Put in the last clear events numbers (previous clear to the end of the slideshow)
endPresentationTime = (($end_time - $join_time)/1000).round(1)
$clearPageTimes[($prev_clear_time..endPresentationTime)] = [$pageCleared, $canvas_number, nil, nil]
# Put the headers on the svg xml file.
$xml.doc.create_internal_subset('svg', "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd")
$xml.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') do
# This is for the first image. It is a placeholder for an image that doesn't exist.
$xml.image(:id => :image0, :in => 0, :out => $first_slide_start, :src => "logo.png", :width => 800)
$xml.g(:class => :canvas, :id => :canvas0, :image => :image0, :display => :none)
$presentation_name = ""
processSlideEvents()
processClearImages()
BigBlueButton.logger.info("Printing out the gathered images")
# Print out the gathered/detected images.
$slides_compiled.each do |key, val|
$val = val
$xml.image(:id => "image#{$val[2].to_i}", :in => $val[0].join(' '), :out => $val[1].join(' '), 'xlink:href' => key[0], :height => key[1], :width => key[2], :visibility => :hidden, :text => $val[3])
$canvas_number+=1
$xml.g(:class => :canvas, :id => "canvas#{$val[2].to_i}", :image => "image#{$val[2].to_i}", :display => :none) do
BigBlueButton.logger.info("Processing shapes within the image")
# Select and print the shapes within the current image
$shape_events.each do |shape|
$shapeTimestamp = shape[:timestamp].to_f
$shapeCreationTime = (($shapeTimestamp-$join_time)/1000).round(1)
in_this_image = false
index = 0
numOfTimes = $val[0].length
# Checks to see if the current shapes are to be drawn in this particular image
while((in_this_image == false) && (index < numOfTimes)) do
if((($val[0][index].to_f)..($val[1][index].to_f)) === $shapeCreationTime) # is the shape within the certain time of the image
in_this_image = true
end
index+=1
end
if(in_this_image)
# Get variables
$shapeType = shape.xpath(".//type")[0].text()
$shapeThickness = shape.xpath(".//thickness")[0].text()
$pageNumber = shape.xpath(".//pageNumber")[0].text()
$shapeDataPoints = shape.xpath(".//dataPoints")[0].text().split(",")
colour = shape.xpath(".//color")[0].text()
if($shapeType == "text")
$textValue = shape.xpath(".//text")[0].text()
$textFontType = shape.xpath(".//font")[0].text()
$textFontSize = shape.xpath(".//fontsize")[0].text()
end
# figure out undo time
BigBlueButton.logger.info("Figuring out undo time")
if($undos.has_key? ((shape[:timestamp].to_f - $join_time)/1000).round(1))
$shapeUndoTime = $undos[((shape[:timestamp].to_f - $join_time)/1000).round(1)]
else
$shapeUndoTime = -1
end
clear_time = -1
$clearPageTimes.each do |clearTimeInstance, pageAndCanvasNumbers|
$clearTimeInstance = clearTimeInstance
$pageAndCanvasNumbers = pageAndCanvasNumbers
if(($clearTimeInstance.last > $shapeCreationTime) && ($pageAndCanvasNumbers[3] == "image#{$val[2].to_i}"))
if((clear_time > $clearTimeInstance.last) || (clear_time == -1))
clear_time = $clearTimeInstance.last
end
end
end
if($shapeUndoTime == -1)
if(clear_time == -1)
$shapeUndoTime = -1 # nothing changes
elsif(clear_time != -1)
$shapeUndoTime = clear_time
end
elsif($shapeUndoTime != -1)
if(clear_time == -1)
$shapeUndoTime = $shapeUndoTime #nothing changes
elsif (clear_time != -1)
if(clear_time < $shapeUndoTime)
$shapeUndoTime = clear_time
else
$shapeUndoTime = $shapeUndoTime # nothing changes
end
end
end
# Process colours
$colour_hex = colour.to_i.to_s(16) # convert from base 10 to base 16 (hex)
$colour_hex='0'*(6-$colour_hex.length) + $colour_hex # pad the number with 0's to give it a length of 6
# resolve the current image height and width
$ss.each do |t,size|
if t === $shapeCreationTime
$vbox_width = size[0]
$vbox_height = size[1]
end
end
# Process the pencil shapes.
if $shapeType.eql? "pencil"
storePencilShape()
# Process the rectangle shapes
elsif $shapeType.eql? "rectangle"
storeRectShape()
# Process the ellipse shapes
elsif $shapeType.eql? "ellipse"
storeEllipseShape()
elsif $shapeType.eql? "text"
storeTextShape()
end # end if pencil (and other shapes)
end # end if((in_this_image) && (in_this_canvas))
end # end shape_events.each do |shape|
end
end
end
end
end
def processChatMessages
BigBlueButton.logger.info("Processing chat events")
# Create slides.xml and chat.
$slides_doc = Nokogiri::XML::Builder.new do |xml|
$xml = xml
$xml.popcorn {
# Process chat events.
$chat_events.each do |node|
chat_timestamp = node[:timestamp]
chat_sender = node.xpath(".//sender")[0].text()
chat_message = BigBlueButton::Events.linkify(node.xpath(".//message")[0].text())
chat_start = (chat_timestamp.to_i - $meeting_start.to_i) / 1000
$xml.chattimeline(:in => chat_start, :direction => :down, :name => chat_sender, :message => chat_message, :target => :chat )
end
}
end
end
$vbox_width = 1600
$vbox_height = 1200
$magic_mystery_number = 2
$shapesold_svg_filename = 'shapes_old.svg'
$shapes_svg_filename = 'shapes.svg'
$panzooms_xml_filename = 'panzooms.xml'
$cursor_xml_filename = 'cursor.xml'
$originX = "NaN"
$originY = "NaN"
$originalOriginX = "NaN"
$originalOriginY = "NaN"
$rectangle_count = 0
$line_count = 0
$ellipse_count = 0
$text_count = 0
$global_slide_count = 1
$global_page_count = 0
$canvas_number = 0
$prev_clear_time = 0
$pageCleared = "0"
$page_number = 0
$prev_canvas_time_start = 0 # initial start is 0 seconds. (beginning of video)
$prev_time = "NaN"
$ss = {}
$clearPageTimes = {}
$slides_compiled = {}
$undos = {}
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]
puts $meeting_id
puts $playback
if ($playback == "slides")
logger = Logger.new("/var/log/bigbluebutton/presentation/publish-#{$meeting_id}.log", 'daily' )
BigBlueButton.logger = logger
BigBlueButton.logger.info("RUNNING SLIDES_NEW.RB - Publishing #{$meeting_id}")
# This script lives in scripts/archive/steps while properties.yaml lives in scripts/
bbb_props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))
simple_props = YAML::load(File.open('presentation.yml'))
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 = simple_props['publish_dir']
BigBlueButton.logger.info("setting playback host")
playback_host = simple_props['playback_host']
BigBlueButton.logger.info("setting target dir")
target_dir = "#{recording_dir}/publish/presentation/#{$meeting_id}"
if not 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")
FileUtils.mkdir_p package_dir
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.ogg to -> #{audio_dir}")
FileUtils.cp("#{$process_dir}/audio.ogg", audio_dir)
BigBlueButton.logger.info("Copied .ogg file - copying: #{$process_dir}/temp/#{$meeting_id}/audio/recording.wav to -> #{audio_dir}")
FileUtils.cp("#{$process_dir}/temp/#{$meeting_id}/audio/recording.wav", audio_dir)
BigBlueButton.logger.info("Copied .wav file - copying #{$process_dir}/events.xml to -> #{package_dir}")
FileUtils.cp("#{$process_dir}/events.xml", package_dir)
BigBlueButton.logger.info("Copied events.xml file")
BigBlueButton.logger.info("Making video dir")
video_dir = "#{package_dir}/video"
FileUtils.mkdir_p video_dir
BigBlueButton.logger.info("Made video dir - copying: #{$process_dir}/webcams.webm to -> #{video_dir}")
FileUtils.cp("#{$process_dir}/webcams.webm", video_dir)
BigBlueButton.logger.info("Copied .webm file")
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("Creating metadata.xml")
# Create metadata.xml
b = Builder::XmlMarkup.new(:indent => 2)
metaxml = b.recording {
b.id($meeting_id)
b.state("available")
b.published(true)
# Date Format for recordings: Thu Mar 04 14:05:56 UTC 2010
b.start_time(BigBlueButton::Events.first_event_timestamp("#{$process_dir}/events.xml"))
b.end_time(BigBlueButton::Events.last_event_timestamp("#{$process_dir}/events.xml"))
b.playback {
b.format("presentation")
b.link("http://#{playback_host}/playback/presentation/playback.html?meetingId=#{$meeting_id}")
}
b.meta {
BigBlueButton::Events.get_meeting_metadata("#{$process_dir}/events.xml").each { |k,v| b.method_missing(k,v) }
}
}
metadata_xml = File.new("#{package_dir}/metadata.xml","w")
metadata_xml.write(metaxml)
metadata_xml.close
BigBlueButton.logger.info("Generating xml for slides and chat")
#Create slides.xml
# presentation_url = "/slides/" + $meeting_id + "/presentation"
@doc = Nokogiri::XML(File.open("#{$process_dir}/events.xml"))
$meeting_start = @doc.xpath("//event[@eventname='ParticipantJoinEvent']")[0][:timestamp]
$meeting_end = @doc.xpath("//event[@eventname='EndAndKickAllEvent']").last()[:timestamp]
first_presentation_start_node = @doc.xpath("//event[@eventname='SharePresentationEvent']")
first_presentation_start = $meeting_end
if not first_presentation_start_node.empty?
first_presentation_start = first_presentation_start_node[0][:timestamp]
end
$first_slide_start = ((first_presentation_start.to_f - $meeting_start.to_f) / 1000).round(1)
# Gathering all the events from the events.xml
$slides_events = @doc.xpath("//event[@eventname='GotoSlideEvent' or @eventname='SharePresentationEvent']")
$chat_events = @doc.xpath("//event[@eventname='PublicChatEvent']")
$shape_events = @doc.xpath("//event[@eventname='AddShapeEvent']") # for the creation of shapes
$panzoom_events = @doc.xpath("//event[@eventname='ResizeAndMoveSlideEvent']") # for the action of panning and/or zooming
$cursor_events = @doc.xpath("//event[@eventname='CursorMoveEvent']")
$clear_page_events = @doc.xpath("//event[@eventname='ClearPageEvent']") # for clearing the svg image
$undo_events = @doc.xpath("//event[@eventname='UndoShapeEvent']") # for undoing shapes.
$join_time = @doc.xpath("//event[@eventname='ParticipantJoinEvent']")[0][:timestamp].to_f
$end_time = @doc.xpath("//event[@eventname='EndAndKickAllEvent']")[0][:timestamp].to_f
processChatMessages()
processShapesAndClears()
processPanAndZooms()
processCursorEvents()
# Write slides.xml to file
File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts $slides_doc.to_xml }
# Write shapes.svg to file
File.open("#{package_dir}/#{$shapes_svg_filename}", 'w') { |f| f.puts $shapes_svg.to_xml.gsub(%r"\s*\", "") } #.gsub(%r"\s*\\s*\", "") }
# Write panzooms.xml to file
File.open("#{package_dir}/#{$panzooms_xml_filename}", 'w') { |f| f.puts $panzooms_xml.to_xml }
# Write panzooms.xml to file
File.open("#{package_dir}/#{$cursor_xml_filename}", 'w') { |f| f.puts $cursor_xml.to_xml }
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.cp_r(package_dir, publish_dir) # Copy all the files.
BigBlueButton.logger.info("Finished publishing script presentation.rb successfully.")
else
BigBlueButton.logger.info("#{target_dir} is already there")
end
end
performance_end = Time.now