2012-07-18 03:39:44 +08:00
# 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 <http://www.gnu.org/licenses/>.
#
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
2016-01-20 03:52:28 +08:00
#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 = nil
2017-02-24 02:21:10 +08:00
desksharing = false
2016-01-20 03:52:28 +08:00
if $panzoom_events . empty?
BigBlueButton . logger . info ( " No panzoom events; old recording? " )
BigBlueButton . logger . info ( " Synthesizing a panzoom event " )
if ! $slides_events . empty?
timestamp_orig = $slides_events . first [ :timestamp ] . to_f
# make sure this is scheduled *after* the slide is shown. Dunno if needed.
timestamp_orig += 1000
timestamp = ( translateTimestamp ( timestamp_orig ) / 1000 ) . round ( 1 )
$xml . event ( :timestamp = > timestamp , :orig = > timestamp_orig ) do
$xml . viewBox " 0 0 #{ $vbox_width } #{ $vbox_height } "
end
timestamp_orig_prev = timestamp_orig
timestamp_prev = timestamp
h_ratio_prev = 100
w_ratio_prev = 100
x_prev = 0
y_prev = 0
else
BigBlueButton . logger . info ( " Couldn't find any slides! panzooms will be empty. " )
end
else
last_time = $panzoom_events . last [ :timestamp ] . to_f
end
2017-02-24 02:21:10 +08:00
$panzoom_events . each_with_index do | node , index |
2016-01-20 03:52:28 +08:00
# Get variables
2017-02-24 02:21:10 +08:00
timestamp_orig = node [ :timestamp ] . to_f
2016-01-20 03:52:28 +08:00
timestamp = ( translateTimestamp ( timestamp_orig ) / 1000 ) . round ( 1 )
2017-02-24 02:21:10 +08:00
eventname = node [ 'eventname' ]
if eventname == " DeskshareStartedEvent "
desksharing = true
next
elsif eventname == " DeskshareStoppedEvent "
desksharing = false
last_panzoom = getFirstPanAndZoomBeforeDeskshare ( index )
if last_panzoom == nil
$xml . event ( :timestamp = > timestamp , :orig = > timestamp_orig ) do
$xml . viewBox " 0.0 0.0 #{ $vbox_width } .0 #{ $vbox_height } .0 "
end
timestamp_orig_prev = timestamp_orig
timestamp_prev = timestamp
h_ratio_prev = 100
w_ratio_prev = 100
x_prev = 0
y_prev = 0
next
end
h_ratio = last_panzoom . xpath ( " .//heightRatio " ) [ 0 ] . text ( )
w_ratio = last_panzoom . xpath ( " .//widthRatio " ) [ 0 ] . text ( )
x = last_panzoom . xpath ( " .//xOffset " ) [ 0 ] . text ( )
y = last_panzoom . xpath ( " .//yOffset " ) [ 0 ] . text ( )
else
h_ratio = node . xpath ( " .//heightRatio " ) [ 0 ] . text ( )
w_ratio = node . xpath ( " .//widthRatio " ) [ 0 ] . text ( )
x = node . xpath ( " .//xOffset " ) [ 0 ] . text ( )
y = node . xpath ( " .//yOffset " ) [ 0 ] . text ( )
end
# We need to skip this if in the middle of deskshare
next if desksharing
2016-01-20 03:52:28 +08:00
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
$xml . event ( :timestamp = > timestamp_prev , :orig = > timestamp_orig_prev ) do
2016-08-12 23:53:01 +08:00
$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
2016-01-20 03:52:28 +08:00
end
end
BigBlueButton . logger . info ( " Finished creating panzooms.xml " )
2012-07-18 03:39:44 +08:00
end
2017-02-24 02:21:10 +08:00
def getFirstPanAndZoomBeforeDeskshare ( index )
return nil if index < 0
deskshare_started_found = false
while index > = 0 do
eventname = $panzoom_events [ index ] [ 'eventname' ]
if eventname == " DeskshareStartedEvent "
deskshare_started_found = true
else
if deskshare_started_found and eventname == " ResizeAndMoveSlideEvent "
return $panzoom_events [ index ]
end
deskshare_started_found = false
end
index -= 1
end
return nil
end
2016-12-16 01:19:10 +08:00
def scaleToDeskshareVideo ( width , height )
deskshare_video_height = 720 . to_f
deskshare_video_width = 1280 . to_f
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
end
2017-02-24 04:17:16 +08:00
def getDeskshareVideoDimension ( deskshare_stream_name )
2017-02-24 02:21:10 +08:00
video_width = 1280
video_height = 720
2017-02-24 04:17:16 +08:00
deskshare_video_filename = " #{ $deskshare_dir } / #{ deskshare_stream_name } "
if File . exist? ( deskshare_video_filename )
video_width = BigBlueButton . get_video_width ( deskshare_video_filename )
video_height = BigBlueButton . get_video_height ( deskshare_video_filename )
video_width , video_height = scaleToDeskshareVideo ( video_width , video_height )
2016-12-13 02:29:06 +08:00
else
2017-02-24 04:17:16 +08:00
BigBlueButton . logger . error ( " Could not find deskshare video: #{ deskshare_video_filename } " )
2016-12-13 02:29:06 +08:00
end
2017-02-24 04:17:16 +08:00
2017-02-24 02:21:10 +08:00
return video_width , video_height
2016-11-30 01:08:28 +08:00
end
2012-07-18 03:39:44 +08:00
def processCursorEvents
2016-01-20 03:52:28 +08:00
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 = ( translateTimestamp ( timestamp_orig ) / 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 )
$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
xPoint = ( $vbox_width . to_f * x . to_f ) . round ( 1 )
yPoint = ( $vbox_height . to_f * y . to_f ) . round ( 1 )
if xPoint < 800 and yPoint < 600 and xPoint > 0 and yPoint > 0
$xml . event ( :timestamp = > timestamp_prev , :orig = > timestamp_orig_prev ) do
$xml . cursor " #{ xPoint } #{ yPoint } "
end
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 " )
2012-07-18 03:39:44 +08:00
end
def processClearEvents
2016-01-20 03:52:28 +08:00
# process all the cleared pages events.
$clear_page_events . each do | clearEvent |
#Retrieve time, page and presentation.
clearTime = clearEvent [ :timestamp ] . to_f
#clearTime = ( clearEvent[:timestamp].to_f / 1000 ).round(1)
$pageCleared = clearEvent . xpath ( " .//pageNumber " ) [ 0 ] . text ( )
slideFolder = clearEvent . xpath ( " .//presentation " ) [ 0 ] . text ( )
2016-12-15 00:50:34 +08:00
whiteboardId = clearEvent . xpath ( " .//whiteboardId " ) [ 0 ] . text ( )
2016-01-20 03:52:28 +08:00
if $version_atleast_0_9_0
2016-12-15 00:50:34 +08:00
if ( whiteboardId == " deskshare " )
$clearPageTimes [ ( $prev_clear_time .. clearTime ) ] =
[ $pageCleared , $canvas_number , " presentation/deskshare/slide-1.png " , nil ]
else
$clearPageTimes [ ( $prev_clear_time .. clearTime ) ] =
[ $pageCleared , $canvas_number , " presentation/ #{ slideFolder } /slide- #{ $pageCleared . to_i + 1 } .png " , nil ]
end
2016-01-20 03:52:28 +08:00
else
$clearPageTimes [ ( $prev_clear_time .. clearTime ) ] =
[ $pageCleared , $canvas_number , " presentation/ #{ slideFolder } /slide- #{ $pageCleared } .png " , nil ]
end
$prev_clear_time = clearTime
$canvas_number += 1
end
2012-07-18 03:39:44 +08:00
end
def processUndoEvents
2016-01-20 03:52:28 +08:00
# 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 ]
end
end
$undos_temp = { }
$undos . each do | un , val |
$undos_temp [ un [ :timestamp ] ] = val
end
$undos = $undos_temp
BigBlueButton . logger . info ( " Undos: #{ $undos } " )
2012-07-18 03:39:44 +08:00
end
def processClearImages
2016-01-20 03:52:28 +08:00
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
2012-07-18 03:39:44 +08:00
end
def storePencilShape
2016-01-20 03:52:28 +08:00
$pencil_count = $pencil_count + 1 # always update the line count!
$global_shape_count += 1
2017-08-04 00:17:48 +08:00
if $shapeDataPoints . length == 2
# Degenerate path, draw a circle (point) instead
$xml . g ( class : :shape ,
id : " draw #{ $global_shape_count } " ,
timestamp : $shapeCreationTime , undo : $shapeUndoTime ,
shape : " line #{ $pencil_count } " ,
style : " stroke:none;fill: # #{ $color_hex } ;visibility:hidden " ) do
$xml . circle ( cx : $shapeDataPoints [ 0 ] . to_f / 100 * $vbox_width ,
cy : $shapeDataPoints [ 1 ] . to_f / 100 * $vbox_height ,
r : $shapeThickness )
end
else
path = [ ]
dataPoints = $shapeDataPoints . dup
2017-08-04 00:24:35 +08:00
if $shapeCommands
2017-08-04 00:17:48 +08:00
# BBB 2.0 recording, we have a path with commands that has to be converted to SVG path
2017-08-04 00:24:35 +08:00
$shapeCommands . each do | command |
2017-08-04 00:17:48 +08:00
case command
when 1 # MOVE_TO
x = dataPoints . shift . to_f / 100 * $vbox_width
y = dataPoints . shift . to_f / 100 * $vbox_height
path . push ( " M #{ x } #{ y } " )
when 2 # LINE_TO
x = dataPoints . shift . to_f / 100 * $vbox_width
y = dataPoints . shift . to_f / 100 * $vbox_height
path . push ( " L #{ x } #{ y } " )
when 3 # Q_CURVE_TO
cx1 = dataPoints . shift . to_f / 100 * $vbox_width
cy1 = dataPoints . shift . to_f / 100 * $vbox_height
x = dataPoints . shift . to_f / 100 * $vbox_width
y = dataPoints . shift . to_f / 100 * $vbox_height
path . push ( " Q #{ cx1 } #{ cy2 } , #{ x } #{ y } " )
when 4 # C_CURVE_TO
cx1 = dataPoints . shift . to_f / 100 * $vbox_width
cy1 = dataPoints . shift . to_f / 100 * $vbox_height
cx2 = dataPoints . shift . to_f / 100 * $vbox_width
cy2 = dataPoints . shift . to_f / 100 * $vbox_height
x = dataPoints . shift . to_f / 100 * $vbox_width
y = dataPoints . shift . to_f / 100 * $vbox_height
path . push ( " C #{ cx1 } #{ cy1 } , #{ cx2 } #{ cy2 } , #{ x } #{ y } " )
else
raise " Unknown pencil command: #{ command } "
end
end
else
x = dataPoints . shift . to_f / 100 * $vbox_width
y = dataPoints . shift . to_f / 100 * $vbox_height
path . push ( " M #{ x } #{ y } " )
while dataPoints . length > 0
x = dataPoints . shift . to_f / 100 * $vbox_width
y = dataPoints . shift . to_f / 100 * $vbox_height
path . push ( " L #{ x } #{ y } " )
end
end
path = path . join ( " " )
$xml . g ( class : :shape ,
id : " draw #{ $global_shape_count } " ,
timestamp : $shapeCreationTime , undo : $shapeUndoTime ,
shape : " line #{ $pencil_count } " ,
style : " stroke: # #{ $colour_hex } ; stroke-linejoin: round; stroke-linecap: round; stroke-width: #{ $shapeThickness } ; fill: none; visibility:hidden " ) do
$xml . path ( d : path )
2016-01-20 03:52:28 +08:00
end
end
2012-07-18 03:39:44 +08:00
end
2012-09-07 03:21:55 +08:00
def storeLineShape
2016-01-20 03:52:28 +08:00
if ( ( $originalOriginX == ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width ) && ( $originalOriginY == ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height ) )
# do not update the line count
else
$line_count = $line_count + 1
end
$global_shape_count += 1
2016-08-12 23:53:01 +08:00
$xml . g ( :class = > :shape , :id = > " draw #{ $global_shape_count } " , :timestamp = > $shapeCreationTime , :undo = > $shapeUndoTime , :shape = > " line #{ $line_count } " , :style = > " stroke: \# #{ $colour_hex } ; stroke-linecap: round; stroke-width: #{ $shapeThickness } ; visibility:hidden; fill:none " ) do
2016-01-20 03:52:28 +08:00
$originX = ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width
$originY = ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height
endPointX = ( ( $shapeDataPoints [ 2 ] . to_f ) / 100 ) * $vbox_width
endPointY = ( ( $shapeDataPoints [ 3 ] . to_f ) / 100 ) * $vbox_height
$originalOriginX = $originX
$originalOriginY = $originY
$xml . line ( :x1 = > $originX , :y1 = > $originY , :x2 = > endPointX , :y2 = > endPointY )
$prev_time = $shapeCreationTime
end
2012-09-07 03:21:55 +08:00
end
2012-07-18 03:39:44 +08:00
def storeRectShape
2016-01-20 03:52:28 +08:00
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
$global_shape_count += 1
2016-08-12 23:53:01 +08:00
$xml . g ( :class = > :shape , :id = > " draw #{ $global_shape_count } " , :timestamp = > $shapeCreationTime , :undo = > $shapeUndoTime , :shape = > " rect #{ $rectangle_count } " , :style = > " stroke: \# #{ $colour_hex } ; stroke-linejoin: round; stroke-width: #{ $shapeThickness } ; visibility:hidden; fill:none " ) do
$originX = x1 = ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width
$originY = y1 = ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height
x2 = ( ( $shapeDataPoints [ 2 ] . to_f ) / 100 ) * $vbox_width
y2 = ( ( $shapeDataPoints [ 3 ] . to_f ) / 100 ) * $vbox_height
2016-01-20 03:52:28 +08:00
2016-08-12 23:53:01 +08:00
width = ( x2 - x1 ) . abs
height = ( y2 - y1 ) . abs
# Convert to a square, keeping aligned with the start point
2016-01-20 03:52:28 +08:00
if $is_square == " true "
2016-08-12 23:53:01 +08:00
# This duplicates a bug in the BigBlueButton client
if x2 > x1
y2 = y1 + width
else
y2 = y1 - width
end
2016-01-20 03:52:28 +08:00
end
2016-08-12 23:53:01 +08:00
$xml . polygon ( :points = > " #{ x1 } , #{ y1 } #{ x2 } , #{ y1 } #{ x2 } , #{ y2 } #{ x1 } , #{ y2 } " )
$originalOriginX = x1
$originalOriginY = y1
2016-01-20 03:52:28 +08:00
$prev_time = $shapeCreationTime
end
2012-07-18 03:39:44 +08:00
end
2012-09-04 09:14:29 +08:00
def storeTriangleShape
2016-01-20 03:52:28 +08:00
if ( ( $originalOriginX == ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width ) && ( $originalOriginY == ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height ) )
# do not update the triangle count
else
$triangle_count = $triangle_count + 1
end
$global_shape_count += 1
2016-08-12 23:53:01 +08:00
$xml . g ( :class = > :shape , :id = > " draw #{ $global_shape_count } " , :timestamp = > $shapeCreationTime , :undo = > $shapeUndoTime , :shape = > " triangle #{ $triangle_count } " , :style = > " stroke: \# #{ $colour_hex } ; stroke-linejoin: round; stroke-width: #{ $shapeThickness } ; visibility:hidden; fill:none " ) do
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
$originX = ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width
$originY = ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
#3 points (p0, p1 and p2) to draw a triangle
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
base = ( ( $shapeDataPoints [ 2 ] . to_f - $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
x0 = $originX + ( base . to_f / 2 . 0 )
x1 = $originX
x2 = $originX + base . to_f
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
height = ( ( $shapeDataPoints [ 3 ] . to_f - $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
y0 = $originY
y1 = $originY + height
y2 = y1
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
p0 = " #{ x0 } , #{ y0 } "
p1 = " #{ x1 } , #{ y1 } "
p2 = " #{ x2 } , #{ y2 } "
2012-09-04 09:14:29 +08:00
2016-01-20 03:52:28 +08:00
$originalOriginX = $originX
$originalOriginY = $originY
2012-09-04 09:14:29 +08:00
2016-08-12 23:53:01 +08:00
$xml . polygon ( :points = > " #{ p0 } #{ p1 } #{ p2 } " )
2016-01-20 03:52:28 +08:00
$prev_time = $shapeCreationTime
end
2012-09-04 09:14:29 +08:00
end
2012-07-18 03:39:44 +08:00
def storeEllipseShape
2016-01-20 03:52:28 +08:00
if ( ( $originalOriginX == ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width ) && ( $originalOriginY == ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height ) )
2016-08-12 23:53:01 +08:00
# do not update the ellipse count
2016-01-20 03:52:28 +08:00
else
$ellipse_count = $ellipse_count + 1
end # end (($originalOriginX == (($shapeDataPoints[0].to_f)/100)*$vbox_width) && ($originalOriginY == (($shapeDataPoints[1].to_f)/100)*$vbox_height))
$global_shape_count += 1
2016-08-12 23:53:01 +08:00
$xml . g ( :class = > :shape , :id = > " draw #{ $global_shape_count } " , :timestamp = > $shapeCreationTime , :undo = > $shapeUndoTime , :shape = > " ellipse #{ $ellipse_count } " , :style = > " stroke: \# #{ $colour_hex } ; stroke-linejoin: round; stroke-width: #{ $shapeThickness } ; visibility:hidden; fill:none " ) do
$originX = x1 = ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width
$originY = y1 = ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height
x2 = ( ( $shapeDataPoints [ 2 ] . to_f ) / 100 ) * $vbox_width
y2 = ( ( $shapeDataPoints [ 3 ] . to_f ) / 100 ) * $vbox_height
width_r = ( x2 - x1 ) . abs / 2
height_r = ( y2 - y1 ) . abs / 2
# Convert to a circle, keeping aligned with the start point
2016-01-20 03:52:28 +08:00
if $is_circle == " true "
2016-08-12 23:53:01 +08:00
height_r = width_r
# This duplicates a bug in the BigBlueButton client
if x2 > x1
y2 = y1 + width_r + width_r
else
y2 = y1 - width_r - width_r
end
2016-01-20 03:52:28 +08:00
end
2016-08-12 23:53:01 +08:00
# SVG's ellipse element doesn't render if r_x or r_y is 0, but
# 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.
# Normalize the x,y coordinates
x1 , x2 = x2 , x1 if x1 > x2
y1 , y2 = y2 , y1 if y1 > y2
path = " M #{ x1 } , #{ ( y1 + y2 ) / 2 } "
path += " A #{ width_r } , #{ height_r } 0 0 1 #{ ( x1 + x2 ) / 2 } , #{ y1 } "
path += " A #{ width_r } , #{ height_r } 0 0 1 #{ x2 } , #{ ( y1 + y2 ) / 2 } "
path += " A #{ width_r } , #{ height_r } 0 0 1 #{ ( x1 + x2 ) / 2 } , #{ y2 } "
path += " A #{ width_r } , #{ height_r } 0 0 1 #{ x1 } , #{ ( y1 + y2 ) / 2 } "
path += " Z "
$xml . path ( :d = > path )
$originalOriginX = $originX
$originalOriginY = $originY
2016-01-20 03:52:28 +08:00
$prev_time = $shapeCreationTime
end # end xml.g
2013-09-26 21:32:55 +08:00
end
2013-09-27 00:06:15 +08:00
def storeTextShape
2016-01-20 03:52:28 +08:00
$originX = ( ( $shapeDataPoints [ 0 ] . to_f ) / 100 ) * $vbox_width
$originY = ( ( $shapeDataPoints [ 1 ] . to_f ) / 100 ) * $vbox_height
if ( ( $originalOriginX == $originX ) && ( $originalOriginY == $originY ) )
# do not update the text count
else
$text_count = $text_count + 1
end
2017-04-26 22:18:08 +08:00
width = ( ( $textBoxWidth . to_f ) / 100 . 0 ) * $vbox_width
height = ( ( $textBoxHeight . to_f ) / 100 . 0 ) * $vbox_height
$textFontSize_pixels = ( ( $textCalcedFontSize . to_f * $vbox_height ) / 100 )
2016-01-20 03:52:28 +08:00
$global_shape_count += 1
$xml . g ( :class = > :shape , :id = > " draw #{ $global_shape_count } " , :timestamp = > $shapeCreationTime , :undo = > $shapeUndoTime , :shape = > " text #{ $text_count } " , :style = > " word-wrap: break-word; visibility:hidden; font-family: #{ $textFontType } ; font-size: #{ $textFontSize_pixels } px; " ) do
$xml . switch do
2017-04-26 22:18:08 +08:00
$xml . foreignObject ( :color = > " # #{ $colour_hex } " , :width = > width , :height = > height , :x = > " #{ $originX } " , :y = > " #{ $originY } " ) do
$xml . p ( :xmlns = > " http://www.w3.org/1999/xhtml " , :style = > " margin-top: 0; margin-bottom: 0; " ) do
2017-04-27 02:59:55 +08:00
lines = $textValue . split ( " \n " )
lines . each_with_index do | line , index |
$xml . text ( line )
if index + 1 < lines . size
$xml . br
end
end
2016-01-20 03:52:28 +08:00
end
end
end
$prev_time = $shapeCreationTime
end # end xml.g
$originalOriginX = $originX
$originalOriginY = $originY
2012-07-18 03:39:44 +08:00
end
2015-08-12 02:55:59 +08:00
def storePollResultShape ( xml , shape )
origin_x = $shapeDataPoints [ 0 ] . to_f / 100 * $vbox_width
origin_y = $shapeDataPoints [ 1 ] . to_f / 100 * $vbox_height
width = $shapeDataPoints [ 2 ] . to_f / 100 * $vbox_width
height = $shapeDataPoints [ 3 ] . to_f / 100 * $vbox_height
result = JSON . load ( shape . at_xpath ( 'result' ) . text )
num_responders = shape . at_xpath ( 'num_responders' ) . text . to_i
presentation = shape . at_xpath ( 'presentation' ) . text
2017-04-07 02:33:38 +08:00
max_num_votes = result . map { | r | r [ 'num_votes' ] } . max
2015-08-12 02:55:59 +08:00
$global_shape_count += 1
$poll_result_count += 1
dat_file = " #{ $process_dir } /poll_result #{ $poll_result_count } .dat "
gpl_file = " #{ $process_dir } /poll_result #{ $poll_result_count } .gpl "
pdf_file = " #{ $process_dir } /poll_result #{ $poll_result_count } .pdf "
svg_file = " #{ $process_dir } /presentation/ #{ presentation } /poll_result #{ $poll_result_count } .svg "
# Use gnuplot to generate an SVG image for the graph
File . open ( dat_file , 'w' ) do | d |
result . each do | r |
d . puts ( " #{ r [ 'id' ] } #{ r [ 'num_votes' ] } " )
end
end
File . open ( dat_file , 'r' ) do | d |
BigBlueButton . logger . debug ( " gnuplot data: " )
BigBlueButton . logger . debug ( d . readlines ( nil ) [ 0 ] )
end
File . open ( gpl_file , 'w' ) do | g |
g . puts ( 'reset' )
2016-03-08 00:26:17 +08:00
g . puts ( " set term pdfcairo size #{ height / 72 } , #{ width / 72 } font \" Arial,48 \" noenhanced " )
2017-04-07 06:04:10 +08:00
g . puts ( 'set lmargin 0.5' )
g . puts ( 'set rmargin 0.5' )
2015-08-12 02:55:59 +08:00
g . puts ( 'unset key' )
g . puts ( 'set style data boxes' )
g . puts ( 'set style fill solid border -1' )
g . puts ( 'set boxwidth 0.9 relative' )
g . puts ( 'set yrange [0:*]' )
g . puts ( 'unset border' )
g . puts ( 'unset ytics' )
2016-03-08 00:26:17 +08:00
xtics = result . map { | r | " #{ r [ 'key' ] . gsub ( '%' , '%%' ) . inspect } #{ r [ 'id' ] } " } . join ( ', ' )
2015-08-12 02:55:59 +08:00
g . puts ( " set xtics rotate by 90 scale 0 right ( #{ xtics } ) " )
2015-08-25 02:42:31 +08:00
if num_responders > 0
x2tics = result . map { | r | " \" #{ ( r [ 'num_votes' ] . to_f / num_responders * 100 ) . to_i } %% \" #{ r [ 'id' ] } " } . join ( ', ' )
g . puts ( " set x2tics rotate by 90 scale 0 left ( #{ x2tics } ) " )
end
2015-08-12 02:55:59 +08:00
g . puts ( 'set linetype 1 linewidth 1 linecolor rgb "black"' )
result . each do | r |
2017-04-07 02:33:38 +08:00
if r [ 'num_votes' ] == 0 or r [ 'num_votes' ] . to_f / max_num_votes < = 0 . 5
2015-08-12 02:55:59 +08:00
g . puts ( " set label \" #{ r [ 'num_votes' ] } \" at #{ r [ 'id' ] } , #{ r [ 'num_votes' ] } left rotate by 90 offset 0,character 0.5 front " )
else
g . puts ( " set label \" #{ r [ 'num_votes' ] } \" at #{ r [ 'id' ] } , #{ r [ 'num_votes' ] } right rotate by 90 offset 0,character -0.5 textcolor rgb \" white \" front " )
end
end
g . puts ( " set output \" #{ pdf_file } \" " )
g . puts ( " plot \" #{ dat_file } \" " )
end
File . open ( gpl_file , 'r' ) do | d |
BigBlueButton . logger . debug ( " gnuplot script: " )
BigBlueButton . logger . debug ( d . readlines ( nil ) [ 0 ] )
end
ret = BigBlueButton . exec_ret ( 'gnuplot' , '-d' , gpl_file )
raise " Failed to generate plot pdf " if ret != 0
ret = BigBlueButton . exec_ret ( 'pdftocairo' , '-svg' , pdf_file , svg_file )
raise " Failed to convert poll to svg " if ret != 0
xml . g ( class : 'shape' , id : " draw #{ $global_shape_count } " ,
shape : " poll_result #{ $poll_result_count } " , style : 'visibility:hidden' ,
timestamp : $shapeCreationTime , undo : $shapeUndoTime ) do
# Outer box to act as poll result backdrop
xml . rect ( x : origin_x + 2 , y : origin_y + 2 , width : width - 4 , height : height - 4 ,
'fill' = > 'white' , 'stroke' = > 'black' , 'stroke-width' = > 4 )
# Poll image
xml . image ( 'xlink:href' = > " presentation/ #{ presentation } /poll_result #{ $poll_result_count } .svg " ,
height : width , width : height , x : $vbox_width , y : origin_y ,
transform : " rotate(90, #{ $vbox_width } , #{ origin_y } ) " )
end
end
2013-09-20 02:22:22 +08:00
#
# Calculate the offsets based on the start and stop recording events, so it's easier
# to translate the timestamps later based on these offsets
#
2013-09-19 04:54:51 +08:00
def calculateRecordEventsOffset
2016-01-20 03:52:28 +08:00
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 [ :accumulated_duration ] = accumulated_duration
previous_stop_recording = event [ :stop_timestamp ]
accumulated_duration += event [ :duration ]
end
2013-09-19 04:54:51 +08:00
end
2013-09-20 02:22:22 +08:00
#
2016-01-20 03:52:28 +08:00
# Translated an arbitrary Unix timestamp to the recording timestamp. This is the
2013-09-20 02:22:22 +08:00
# function that others will call
#
def translateTimestamp ( timestamp )
2016-01-20 03:52:28 +08:00
new_timestamp = translateTimestamp_helper ( timestamp . to_f ) . to_f
2013-12-02 23:57:05 +08:00
# BigBlueButton.logger.info("Translating #{timestamp}, old value=#{timestamp.to_f-$meeting_start.to_f}, new value=#{new_timestamp}")
2016-01-20 03:52:28 +08:00
new_timestamp
2013-09-19 04:54:51 +08:00
end
2013-09-20 02:22:22 +08:00
#
# Translated an arbitrary Unix timestamp to the recording timestamp
#
def translateTimestamp_helper ( timestamp )
2016-01-20 03:52:28 +08:00
$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 ]
# 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 ]
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 ]
2013-09-19 04:54:51 +08:00
end
2013-09-26 21:32:55 +08:00
#
# Given an event timestamp, says whether it occurs during a recording period or not.
#
def occursDuringRecording ( timestamp )
2016-01-20 03:52:28 +08:00
$rec_events . each do | event |
if timestamp > = event [ :start_timestamp ] and timestamp < = event [ :stop_timestamp ]
return true
end
end
return false
2013-09-26 21:32:55 +08:00
end
2013-09-19 04:54:51 +08:00
def preprocessSlideEvents
2016-01-20 03:52:28 +08:00
new_slides_events = [ ]
$slides_events . each do | slide_event |
new_slide_event = slide_event . clone
$rec_events . each do | rec_event |
if new_slide_event [ :timestamp ] < = rec_event [ :start_timestamp ]
new_slide_event [ :timestamp ] = rec_event [ :start_timestamp ]
if not new_slides_events . empty? and new_slides_events . last ( ) [ :timestamp ] == rec_event [ :start_timestamp ]
new_slides_events . pop ( )
end
new_slides_events << new_slide_event
break
elsif new_slide_event [ :timestamp ] > rec_event [ :start_timestamp ] and new_slide_event [ :timestamp ] < = rec_event [ :stop_timestamp ]
new_slides_events << new_slide_event
end
end
end
return new_slides_events
2013-09-19 04:54:51 +08:00
end
2017-02-24 02:21:10 +08:00
def getLastProcessedSlide ( index )
return nil if ( index < 0 )
eventname = $slides_events [ index ] [ 'eventname' ]
while eventname != " GotoSlideEvent " do
index -= 1
return nil if ( index < 0 )
eventname = $slides_events [ index ] [ 'eventname' ]
end
return $slides_events [ index ]
end
2012-07-18 03:39:44 +08:00
def processSlideEvents
2016-01-20 03:52:28 +08:00
BigBlueButton . logger . info ( " Slide events processing " )
2017-02-24 02:21:10 +08:00
deskshare_slide = false
2016-12-03 03:40:50 +08:00
2016-01-20 03:52:28 +08:00
# For each slide (there is only one image per slide)
2017-02-24 02:21:10 +08:00
$slides_events . each_with_index do | node , index |
2016-01-20 03:52:28 +08:00
# Ignore slide events that happened after the last recording period.
if ( node [ :timestamp ] . to_f > $rec_events . last [ :stop_timestamp ] . to_f )
next
end
2017-08-03 04:50:24 +08:00
2016-01-20 03:52:28 +08:00
eventname = node [ 'eventname' ]
2017-08-03 04:50:24 +08:00
slide_timestamp = node [ :timestamp ]
slide_start = ( translateTimestamp ( slide_timestamp ) / 1000 ) . round ( 1 )
orig_slide_start = ( slide_timestamp . to_f / 1000 ) . round ( 1 )
2016-01-20 03:52:28 +08:00
if eventname == " SharePresentationEvent "
$presentation_name = node . xpath ( " .//presentationName " ) [ 0 ] . text ( )
2017-08-03 04:50:24 +08:00
slide_number = $presentation_current_slide [ $presentation_name ] . to_i
BigBlueButton . logger . info ( " Switching to presentation #{ $presentation_name } , saved slide is #{ slide_number } " )
2016-11-30 01:08:28 +08:00
2017-08-03 22:47:30 +08:00
elsif eventname == 'GotoSlideEvent'
2017-08-03 04:50:24 +08:00
slide_number = node . xpath ( " .//slide " ) [ 0 ] . text ( ) . to_i
BigBlueButton . logger . info ( " Switching to slide #{ slide_number } in presentation #{ $presentation_name } " )
2017-02-24 02:21:10 +08:00
2017-08-03 04:50:24 +08:00
elsif eventname == " DeskshareStartedEvent "
deskshare_slide = true
slide_number = - 1
BigBlueButton . logger . info ( " Started deskshare; slide area is cleared " )
elsif eventname == " DeskshareStoppedEvent "
deskshare_slide = false
slide_number = $presentation_current_slide [ $presentation_name ] . to_i
BigBlueButton . logger . info ( " Stopped deskshare; restoring slide #{ slide_number } in presentation #{ $presentation_name } " )
else
BigBlueButton . logger . warn ( " Unhandled event #{ eventname } in processSlideEvents " )
next
end
2016-01-20 03:52:28 +08:00
2017-08-03 04:50:24 +08:00
slide_number = slide_number < 0 ? 0 : slide_number
$presentation_current_slide [ $presentation_name ] = slide_number
slide_src = deskshare_slide ?
" presentation/deskshare/slide-1.png " :
" presentation/ #{ $presentation_name } /slide- #{ slide_number + 1 } .png "
txt_file_path = deskshare_slide ?
" presentation/deskshare/slide-1.txt " :
" presentation/ #{ $presentation_name } /textfiles/slide- #{ slide_number + 1 } .txt "
slide_text = File . exist? ( " #{ $process_dir } / #{ txt_file_path } " ) ? txt_file_path : nil
image_url = " #{ $process_dir } / #{ slide_src } "
if ! File . exist? ( image_url )
BigBlueButton . logger . warn ( " Missing image file #{ slide_src } ! " )
# Emergency last-ditch blank image creation
if deskshare_slide
FileUtils . mkdir_p ( " #{ $process_dir } /presentation/deskshare " )
command = " convert -size 1280x720 xc:transparent -background transparent #{ image_url } "
2016-01-20 03:52:28 +08:00
else
2017-08-03 04:50:24 +08:00
FileUtils . mkdir_p ( " #{ $process_dir } /presentation/ #{ $presentation_name } " )
command = " convert -size 1600x1200 xc:white -quality 90 +dither -depth 8 -colors 256 #{ image_url } "
2016-01-20 03:52:28 +08:00
end
2017-08-03 04:50:24 +08:00
BigBlueButton . execute ( command )
end
2016-01-20 03:52:28 +08:00
2017-08-03 04:50:24 +08:00
slide_size = FastImage . size ( image_url )
if ( index + 1 < $slides_events . length )
slide_end = ( translateTimestamp ( $slides_events [ index + 1 ] [ :timestamp ] ) / 1000 ) . round ( 1 )
orig_slide_end = ( $slides_events [ index + 1 ] [ :timestamp ] . to_f / 1000 ) . round ( 1 )
else
slide_end = ( translateTimestamp ( $meeting_end ) / 1000 ) . round ( 1 )
orig_slide_end = ( $meeting_end . to_f / 1000 ) . round ( 1 )
end
if slide_start == slide_end
BigBlueButton . logger . info ( " Slide is never displayed (slide_start = slide_end), so it won't be included in the svg " )
next
end
2016-01-20 03:52:28 +08:00
2017-08-03 22:40:17 +08:00
BigBlueButton . logger . info ( " Processing slide image: #{ slide_src } : #{ slide_start } -> #{ slide_end } " )
# 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 , [ orig_slide_start ] , [ orig_slide_end ] , $presentation_name , slide_number ]
$global_slide_count = $global_slide_count + 1
else
$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
2016-01-20 03:52:28 +08:00
end
2017-08-03 22:40:17 +08:00
$slides_compiled [ [ slide_src , slide_size [ 1 ] , slide_size [ 0 ] ] ] [ 4 ] << orig_slide_start
$slides_compiled [ [ slide_src , slide_size [ 1 ] , slide_size [ 0 ] ] ] [ 5 ] << orig_slide_end
2017-08-03 22:50:14 +08:00
$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
2012-07-18 03:39:44 +08:00
end
def processShapesAndClears
2016-01-20 03:52:28 +08:00
# 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.to_f / 1000 ).round(1)
endPresentationTime = $end_time . to_f
$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 , :class = > 'slide' , :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 } " , :class = > 'slide' , :in = > $val [ 0 ] . join ( ' ' ) , :out = > $val [ 1 ] . join ( ' ' ) , 'xlink:href' = > key [ 0 ] , :height = > key [ 1 ] , :width = > key [ 2 ] , :visibility = > :hidden , :text = > $val [ 3 ] , :x = > 0 )
$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 #{ $val [ 2 ] . to_i } " )
# Select and print the shapes within the current image
$shape_events . each do | shape |
$shapeTimestamp = shape [ :timestamp ] . to_f
$shapeCreationTime = ( translateTimestamp ( $shapeTimestamp ) / 1000 ) . round ( 1 )
orig_shapeCreationTime = ( $shapeTimestamp . to_f / 1000 ) . round ( 1 )
in_this_image = false
2017-04-26 23:21:29 +08:00
# Check if the current shape is to be drawn in the current image
presentation = shape . at_xpath ( " .//presentation " )
pageNumber = shape . at_xpath ( " .//pageNumber " )
if presentation and pageNumber
# If we have the presentation and page number available, match
# against that.
2017-04-27 02:00:14 +08:00
pageNumber = pageNumber . text ( ) . to_i
pageNumber -= 1 unless $version_atleast_0_9_0
if presentation . text ( ) == $val [ 6 ] and pageNumber == $val [ 7 ]
2016-01-20 03:52:28 +08:00
in_this_image = true
end
2017-04-26 23:21:29 +08:00
else
# Otherwise check if the shape is within one of the time ranges
# when the current image is visible
index = 0
numOfTimes = $val [ 0 ] . length
while ( ( in_this_image == false ) && ( index < numOfTimes ) ) do
if ( ( ( $val [ 4 ] [ index ] . to_f ) .. ( $val [ 5 ] [ index ] . to_f ) ) === orig_shapeCreationTime )
in_this_image = true
end
index += 1
end
2016-01-20 03:52:28 +08:00
end
if ( in_this_image )
2016-01-27 03:25:02 +08:00
# Get variables
BigBlueButton . logger . info shape . to_xml ( :indent = > 2 )
$shapeType = shape . xpath ( " .//type " ) [ 0 ] . text ( )
$pageNumber = shape . xpath ( " .//pageNumber " ) [ 0 ] . text ( )
$shapeDataPoints = shape . xpath ( " .//dataPoints " ) [ 0 ] . text ( ) . split ( " , " )
case $shapeType
2017-08-04 00:17:48 +08:00
when 'pencil'
$shapeThickness = shape . xpath ( " .//thickness " ) [ 0 ] . text ( )
colour = shape . xpath ( " .//color " ) [ 0 ] . text ( )
2017-08-04 00:24:35 +08:00
commands = shape . at_xpath ( './/commands' )
if commands
2017-08-04 00:40:56 +08:00
$shapeCommands = commands . text ( ) . split ( ',' )
2017-08-04 00:17:48 +08:00
else
2017-08-04 00:24:35 +08:00
$shapeCommands = nil
2017-08-04 00:17:48 +08:00
end
when 'rectangle' , 'ellipse' , 'triangle' , 'line'
2016-01-27 03:25:02 +08:00
$shapeThickness = shape . xpath ( " .//thickness " ) [ 0 ] . text ( )
colour = shape . xpath ( " .//color " ) [ 0 ] . text ( )
when 'text'
$textValue = shape . xpath ( " .//text " ) [ 0 ] . text ( )
$textFontType = " Arial "
$textFontSize = shape . xpath ( " .//fontSize " ) [ 0 ] . text ( )
2017-04-26 22:18:08 +08:00
$textCalcedFontSize = shape . xpath ( " .//calcedFontSize " ) [ 0 ] . text ( )
2016-01-27 03:25:02 +08:00
colour = shape . xpath ( " .//fontColor " ) [ 0 ] . text ( )
when 'poll_result'
# Just hand the 'shape' xml object to the poll rendering code.
end
2016-01-20 03:52:28 +08:00
# figure out undo time
BigBlueButton . logger . info ( " Figuring out undo time " )
if ( $undos . has_key? ( shape [ :timestamp ] ) )
$shapeUndoTime = ( translateTimestamp ( $undos [ shape [ :timestamp ] ] ) / 1000 ) . round ( 1 )
else
$shapeUndoTime = - 1
end
clear_time = - 1
$clearPageTimes . each do | clearTimeInstance , pageAndCanvasNumbers |
$clearTimeInstance = clearTimeInstance
$pageAndCanvasNumbers = pageAndCanvasNumbers
if ( ( $clearTimeInstance . last > $shapeTimestamp ) && ( $pageAndCanvasNumbers [ 3 ] == " image #{ $val [ 2 ] . to_i } " ) )
if ( ( clear_time > ( translateTimestamp ( $clearTimeInstance . last ) / 1000 ) . round ( 1 ) ) || ( clear_time == - 1 ) )
clear_time = ( translateTimestamp ( $clearTimeInstance . last ) / 1000 ) . round ( 1 )
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
2017-08-03 23:21:51 +08:00
if $version_atleast_2_0
2017-08-03 23:41:28 +08:00
# Shape thickness is now calculated as a percentage of page width
$shapeThickness = $shapeThickness . to_f * $vbox_width / 100 . 0
2017-08-03 23:21:51 +08:00
end
2016-01-27 03:25:02 +08:00
case $shapeType
when 'pencil'
storePencilShape ( )
when 'line'
storeLineShape ( )
when 'rectangle'
square = shape . xpath ( " .//square " )
if square . length > 0
$is_square = square [ 0 ] . text ( )
else
$is_square = 'false'
end
storeRectShape ( )
when 'triangle'
storeTriangleShape ( )
when 'ellipse'
circle = shape . xpath ( " .//circle " )
if circle . length > 0
$is_circle = circle [ 0 ] . text ( )
else
$is_circle = 'false'
end
storeEllipseShape ( )
when 'text'
$textBoxWidth = shape . xpath ( " .//textBoxWidth " ) [ 0 ] . text ( )
$textBoxHeight = shape . xpath ( " .//textBoxHeight " ) [ 0 ] . text ( )
storeTextShape ( )
when 'poll_result'
storePollResultShape ( $xml , shape )
2016-01-20 03:52:28 +08:00
end
end # end if(in_this_image)
end # end shape_events.each do |shape|
end
end
end
end
2012-07-18 03:39:44 +08:00
end
def processChatMessages
2016-01-20 03:52:28 +08:00
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.
current_time = 0
$rec_events . each do | re |
$chat_events . each do | node |
if ( node [ :timestamp ] . to_i > = re [ :start_timestamp ] and node [ :timestamp ] . to_i < = re [ :stop_timestamp ] )
chat_timestamp = node [ :timestamp ]
chat_sender = node . xpath ( " .//sender " ) [ 0 ] . text ( )
chat_message = BigBlueButton :: Events . linkify ( node . xpath ( " .//message " ) [ 0 ] . text ( ) )
chat_start = ( translateTimestamp ( chat_timestamp ) / 1000 ) . to_i
2016-06-22 05:32:59 +08:00
# Creates a list of the clear timestamps that matter for this message
next_clear_timestamps = $clear_chat_timestamps . select { | e | e > = node [ :timestamp ] }
# If there is none we skip it, or else we add the out time that will remove a message
if next_clear_timestamps . empty?
$xml . chattimeline ( :in = > chat_start , :direction = > :down , :name = > chat_sender , :message = > chat_message , :target = > :chat )
else
chat_end = ( translateTimestamp ( next_clear_timestamps . first ) / 1000 ) . to_i
$xml . chattimeline ( :in = > chat_start , :out = > chat_end , :direction = > :down , :name = > chat_sender , :message = > chat_message , :target = > :chat )
end
2016-01-20 03:52:28 +08:00
end
end
current_time += re [ :stop_timestamp ] - re [ :start_timestamp ]
end
}
end
2012-07-18 03:39:44 +08:00
end
2016-09-10 05:31:27 +08:00
def processDeskshareEvents
2017-02-24 02:21:10 +08:00
BigBlueButton . logger . info ( " Processing deskshare events " )
deskshare_matched_events = BigBlueButton :: Events . get_matched_start_and_stop_deskshare_events ( " #{ $process_dir } /events.xml " )
2016-09-10 05:31:27 +08:00
$deskshare_xml = Nokogiri :: XML :: Builder . new do | xml |
$xml = xml
$xml . recording ( 'id' = > 'deskshare_events' ) do
2017-02-24 02:21:10 +08:00
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 )
2017-05-24 04:02:26 +08:00
if ! BigBlueButton . is_video_valid? ( " #{ $deskshare_dir } / #{ event [ :stream ] } " )
2017-05-08 06:12:34 +08:00
BigBlueButton . logger . warn ( " #{ event [ :stream ] } is not a valid video file, skipping... " )
next
end
2017-02-24 04:17:16 +08:00
video_width , video_height = getDeskshareVideoDimension ( event [ :stream ] )
2017-02-24 02:21:10 +08:00
$xml . event ( :start_timestamp = > start_timestamp ,
:stop_timestamp = > stop_timestamp ,
:video_width = > video_width ,
:video_height = > video_height )
2016-09-10 05:31:27 +08:00
end
end
end
end
end
2012-08-27 21:10:45 +08:00
$vbox_width = 1600
$vbox_height = 1200
2012-07-18 03:39:44 +08:00
$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'
2016-09-10 05:31:27 +08:00
$deskshare_xml_filename = 'deskshare.xml'
2012-07-18 03:39:44 +08:00
$originX = " NaN "
$originY = " NaN "
$originalOriginX = " NaN "
$originalOriginY = " NaN "
$rectangle_count = 0
2012-09-04 09:14:29 +08:00
$triangle_count = 0
2012-09-07 03:21:55 +08:00
$pencil_count = 0
2012-07-18 03:39:44 +08:00
$line_count = 0
$ellipse_count = 0
$text_count = 0
2015-08-12 02:55:59 +08:00
$poll_result_count = 0
2013-08-29 01:51:06 +08:00
$global_shape_count = - 1
2012-07-18 03:39:44 +08:00
$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 = { }
2017-08-03 04:50:24 +08:00
$presentation_current_slide = { }
2012-07-18 03:39:44 +08:00
$slides_compiled = { }
2013-09-26 21:32:55 +08:00
$slides_raw = { }
2012-07-18 03:39:44 +08:00
$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
2014-04-15 06:14:14 +08:00
begin
2016-01-27 03:25:02 +08:00
if ( $playback == " presentation " )
2014-04-04 04:23:49 +08:00
2016-01-27 03:25:02 +08:00
# 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' ) )
2014-04-04 04:23:49 +08:00
log_dir = bbb_props [ 'log_dir' ]
2016-01-27 03:25:02 +08:00
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 } "
BigBlueButton . logger . info ( " setting publish dir " )
publish_dir = simple_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 } "
2016-12-01 02:31:19 +08:00
$deskshare_dir = " #{ recording_dir } /raw/ #{ $meeting_id } /deskshare "
2016-01-27 03:25:02 +08:00
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
begin
if File . exist? ( " #{ $process_dir } /webcams.webm " )
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 " )
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 " )
end
2016-01-20 03:52:28 +08:00
2016-02-02 01:23:39 +08:00
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 )
FileUtils . cp ( caption , package_dir )
end
end
2016-10-21 03:39:35 +08:00
if File . exist? ( " #{ $process_dir } /deskshare.webm " )
BigBlueButton . logger . info ( " Making deskshare dir " )
deskshare_dir = " #{ package_dir } /deskshare "
FileUtils . mkdir_p deskshare_dir
BigBlueButton . logger . info ( " Made deskshare dir - copying: #{ $process_dir } /deskshare.webm to -> #{ deskshare_dir } " )
FileUtils . cp ( " #{ $process_dir } /deskshare.webm " , deskshare_dir )
BigBlueButton . logger . info ( " Copied deskshare.webm file " )
else
BigBlueButton . logger . info ( " Could not copy deskshares.webm: file doesn't exist " )
end
2016-11-29 22:00:19 +08:00
if File . exist? ( " #{ $process_dir } /presentation_text.json " )
FileUtils . cp ( " #{ $process_dir } /presentation_text.json " , package_dir )
end
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
processing_time = File . read ( " #{ $process_dir } /processing_time " )
# 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 ( " #{ $process_dir } /events.xml " ) )
2016-08-12 23:53:01 +08:00
recording_time = BigBlueButton :: Events . get_recording_length ( " #{ $process_dir } /events.xml " )
2016-01-27 03:25:02 +08:00
# presentation_url = "/slides/" + $meeting_id + "/presentation"
@doc = Nokogiri :: XML ( File . open ( " #{ $process_dir } /events.xml " ) )
2016-03-04 00:27:28 +08:00
$meeting_start = @doc . xpath ( " //event " ) [ 0 ] [ :timestamp ]
$meeting_end = @doc . xpath ( " //event " ) . last ( ) [ :timestamp ]
2016-01-27 03:25:02 +08:00
$version = BigBlueButton :: Events . bbb_version ( " #{ $process_dir } /events.xml " )
$version_atleast_0_9_0 = BigBlueButton :: Events . bbb_version_compare ( " #{ $process_dir } /events.xml " , 0 , 9 , 0 )
2017-08-03 23:21:51 +08:00
$version_atleast_2_0 = BigBlueButton :: Events . bbb_version_compare ( " #{ $process_dir } /events.xml " , 2 , 0 , 0 )
2016-01-27 03:25:02 +08:00
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
2016-01-28 23:13:45 +08:00
#### INSTEAD OF CREATING THE WHOLE metadata.xml FILE AGAIN, ONLY ADD <playback>
2016-01-27 06:16:28 +08:00
# Copy metadata.xml from process_dir
FileUtils . cp ( " #{ $process_dir } /metadata.xml " , package_dir )
BigBlueButton . logger . info ( " Copied metadata.xml file " )
2016-02-13 01:08:33 +08:00
# Update state and add playback to metadata.xml
2016-01-27 06:16:28 +08:00
## Load metadata.xml
metadata = Nokogiri :: XML ( File . open ( " #{ package_dir } /metadata.xml " ) )
2016-02-13 01:08:33 +08:00
## Update state
2016-01-27 06:16:28 +08:00
recording = metadata . root
state = recording . at_xpath ( " state " )
state . content = " published "
2016-02-10 06:26:29 +08:00
published = recording . at_xpath ( " published " )
published . content = " true "
2016-01-28 23:13:45 +08:00
## Remove empty playback
2016-01-27 06:16:28 +08:00
metadata . search ( '//recording/playback' ) . each do | playback |
playback . remove
end
2016-02-10 06:26:29 +08:00
## Add the actual playback
2016-09-21 04:46:45 +08:00
presentation = BigBlueButton :: Presentation . get_presentation_for_preview ( " #{ $process_dir } " )
2016-01-27 06:16:28 +08:00
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/0.9.0/playback.html?meetingId= #{ $meeting_id } " )
xml . processing_time ( " #{ processing_time } " )
xml . duration ( " #{ recording_time } " )
2016-09-21 02:10:28 +08:00
unless presentation . empty?
2016-09-20 06:09:31 +08:00
xml . extensions {
xml . preview {
xml . images {
2016-09-28 03:19:53 +08:00
presentation [ :slides ] . each do | key , val |
2016-09-28 04:02:20 +08:00
attributes = { :width = > " 176 " , :height = > " 136 " , :alt = > ( val [ :alt ] != nil ) ? " #{ val [ :alt ] } " : " " }
2016-09-28 03:19:53 +08:00
xml . image ( attributes ) { xml . text ( " #{ playback_protocol } :// #{ playback_host } /presentation/ #{ $meeting_id } /presentation/ #{ presentation [ :id ] } /thumbnails/thumb- #{ key } .png " ) }
2016-09-21 02:10:28 +08:00
end
2016-09-20 06:09:31 +08:00
}
}
}
end
2016-01-27 06:16:28 +08:00
}
end
## Write the new metadata.xml
2016-02-13 01:08:33 +08:00
metadata_file = File . new ( " #{ package_dir } /metadata.xml " , " w " )
2016-01-28 23:13:45 +08:00
metadata = Nokogiri :: XML ( metadata . to_xml ) { | x | x . noblanks }
2016-02-13 01:08:33 +08:00
metadata_file . write ( metadata . root )
metadata_file . close
2016-01-27 06:16:28 +08:00
BigBlueButton . logger . info ( " Added playback to metadata.xml " )
2016-01-27 03:25:02 +08:00
#Create slides.xml
2016-02-13 01:08:33 +08:00
BigBlueButton . logger . info ( " Generating xml for slides and chat " )
2016-01-27 03:25:02 +08:00
# Gathering all the events from the events.xml
2017-02-24 02:21:10 +08:00
$slides_events = @doc . xpath ( " //event[@eventname='GotoSlideEvent' or @eventname='SharePresentationEvent' or @eventname='DeskshareStartedEvent' or @eventname='DeskshareStoppedEvent'] " )
2016-01-27 03:25:02 +08:00
$chat_events = @doc . xpath ( " //event[@eventname='PublicChatEvent'] " )
$shape_events = @doc . xpath ( " //event[@eventname='AddShapeEvent' or @eventname='ModifyTextEvent'] " ) # for the creation of shapes
2017-02-24 02:21:10 +08:00
$panzoom_events = @doc . xpath ( " //event[@eventname='ResizeAndMoveSlideEvent' or @eventname='DeskshareStartedEvent' or @eventname='DeskshareStoppedEvent'] " ) # for the action of panning and/or zooming
2016-01-27 03:25:02 +08:00
$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 = $meeting_start . to_f
$end_time = $meeting_end . to_f
2016-06-22 05:32:59 +08:00
# Create a list of timestamps when the moderator cleared the public chat
2017-02-24 21:46:32 +08:00
$clear_chat_timestamps = [ ]
clear_chat_events = @doc . xpath ( " //event[@eventname='ClearPublicChatEvent'] " )
clear_chat_events . each { | clear | $clear_chat_timestamps << clear [ :timestamp ] }
$clear_chat_timestamps . sort!
2016-03-05 01:40:48 +08:00
2016-01-27 03:25:02 +08:00
calculateRecordEventsOffset ( )
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 = ( translateTimestamp ( first_presentation_start ) / 1000 ) . round ( 1 )
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
processChatMessages ( )
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
processShapesAndClears ( )
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
processPanAndZooms ( )
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
processCursorEvents ( )
2016-01-20 03:52:28 +08:00
2016-09-10 05:31:27 +08:00
processDeskshareEvents ( )
2016-01-27 03:25:02 +08:00
# 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*\<g.*/\>" , " " ) } #.gsub(%r"\s*\<g.*\>\s*\</g\>", "") }
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
# Write panzooms.xml to file
File . open ( " #{ package_dir } / #{ $panzooms_xml_filename } " , 'w' ) { | f | f . puts $panzooms_xml . to_xml }
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
# Write panzooms.xml to file
File . open ( " #{ package_dir } / #{ $cursor_xml_filename } " , 'w' ) { | f | f . puts $cursor_xml . to_xml }
2016-01-20 03:52:28 +08:00
2016-09-10 05:31:27 +08:00
# Write deskshare.xml to file
File . open ( " #{ package_dir } / #{ $deskshare_xml_filename } " , 'w' ) { | f | f . puts $deskshare_xml . to_xml }
2016-01-27 03:25:02 +08:00
BigBlueButton . logger . info ( " Copying files to package dir " )
FileUtils . cp_r ( " #{ $process_dir } /presentation " , package_dir )
BigBlueButton . logger . info ( " Copied files to package dir " )
2016-01-20 03:52:28 +08:00
2016-01-27 03:25:02 +08:00
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
2016-03-02 12:03:15 +08:00
# Get raw size of presentation files
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 )
2016-01-27 03:25:02 +08:00
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 } /* " ) )
rescue Exception = > e
BigBlueButton . logger . error ( e . message )
e . backtrace . each do | traceline |
BigBlueButton . logger . error ( traceline )
end
exit 1
2016-01-20 03:52:28 +08:00
end
2016-01-27 03:25:02 +08:00
publish_done = File . new ( " #{ recording_dir } /status/published/ #{ $meeting_id } -presentation.done " , " w " )
publish_done . write ( " Published #{ $meeting_id } " )
publish_done . close
2014-04-15 06:14:14 +08:00
2016-01-27 03:25:02 +08:00
else
BigBlueButton . logger . info ( " #{ target_dir } is already there " )
end
2016-01-20 03:52:28 +08:00
end
2012-07-18 03:39:44 +08:00
2016-08-12 23:53:01 +08:00
rescue Exception = > e
BigBlueButton . logger . error ( e . message )
e . backtrace . each do | traceline |
BigBlueButton . logger . error ( traceline )
2016-01-27 03:25:02 +08:00
end
publish_done = File . new ( " #{ recording_dir } /status/published/ #{ $meeting_id } -presentation.fail " , " w " )
publish_done . write ( " Failed Publishing #{ $meeting_id } " )
publish_done . close
2012-09-07 03:21:55 +08:00
2016-01-27 03:25:02 +08:00
exit 1
2014-04-15 06:14:14 +08:00
end
2012-11-28 06:41:14 +08:00