Merge remote-tracking branch 'upstream/v2.6.x-release' into fix-tomcat-warnings
This commit is contained in:
commit
4b4bbc4d42
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2017 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.core.record.events
|
||||
|
||||
import org.bigbluebutton.common2.domain.SimpleVoteOutVO
|
||||
import scala.collection.immutable.List
|
||||
import scala.collection.Map
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import spray.json._
|
||||
import DefaultJsonProtocol._
|
||||
|
||||
class AddTldrawShapeWhiteboardRecordEvent extends AbstractWhiteboardRecordEvent {
|
||||
import AddTldrawShapeWhiteboardRecordEvent._
|
||||
|
||||
implicit object AnyJsonFormat extends JsonFormat[Any] {
|
||||
def write(x: Any) = x match {
|
||||
case n: Int => JsNumber(n)
|
||||
case s: String => JsString(s)
|
||||
case d: Double => JsNumber(d)
|
||||
case m: scala.collection.immutable.Map[String, _] => mapFormat[String, Any].write(m)
|
||||
case l: List[_] => listFormat[Any].write(l)
|
||||
case b: Boolean if b == true => JsTrue
|
||||
case b: Boolean if b == false => JsFalse
|
||||
}
|
||||
|
||||
def read(value: JsValue) = {}
|
||||
}
|
||||
|
||||
setEvent("AddTldrawShapeEvent")
|
||||
|
||||
def setUserId(id: String) {
|
||||
eventMap.put(USER_ID, id)
|
||||
}
|
||||
|
||||
def setAnnotationId(id: String) {
|
||||
eventMap.put(SHAPE_ID, id)
|
||||
}
|
||||
|
||||
def addAnnotation(annotation: scala.collection.immutable.Map[String, Any]) {
|
||||
eventMap.put(SHAPE_DATA, annotation.toJson.compactPrint)
|
||||
}
|
||||
}
|
||||
|
||||
object AddTldrawShapeWhiteboardRecordEvent {
|
||||
protected final val USER_ID = "userId"
|
||||
protected final val SHAPE_ID = "shapeId"
|
||||
protected final val SHAPE_DATA = "shapeData"
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2017 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.core.record.events
|
||||
|
||||
class DeleteTldrawShapeRecordEvent extends AbstractWhiteboardRecordEvent {
|
||||
import DeleteTldrawShapeRecordEvent._
|
||||
|
||||
setEvent("DeleteTldrawShapeEvent")
|
||||
|
||||
def setUserId(userId: String) {
|
||||
eventMap.put(USER_ID, userId)
|
||||
}
|
||||
|
||||
def setShapeId(shapeId: String) {
|
||||
eventMap.put(SHAPE_ID, shapeId)
|
||||
}
|
||||
}
|
||||
|
||||
object DeleteTldrawShapeRecordEvent {
|
||||
protected final val USER_ID = "userId"
|
||||
protected final val SHAPE_ID = "shapeId"
|
||||
}
|
@ -32,23 +32,28 @@ class ResizeAndMoveSlideRecordEvent extends AbstractPresentationRecordEvent {
|
||||
eventMap.put(ID, id)
|
||||
}
|
||||
|
||||
def setXCamera(xCamera: Double) {
|
||||
eventMap.put(X_CAMERA, xCamera.toString)
|
||||
def setXOffset(offset: Double) {
|
||||
eventMap.put(X_OFFSET, offset.toString)
|
||||
}
|
||||
|
||||
def setYCamera(yCamera: Double) {
|
||||
eventMap.put(Y_CAMERA, yCamera.toString)
|
||||
def setYOffset(offset: Double) {
|
||||
eventMap.put(Y_OFFSET, offset.toString)
|
||||
}
|
||||
|
||||
def setZoom(zoom: Double) {
|
||||
eventMap.put(ZOOM, zoom.toString)
|
||||
def setWidthRatio(ratio: Double) {
|
||||
eventMap.put(WIDTH_RATIO, ratio.toString)
|
||||
}
|
||||
|
||||
def setHeightRatio(ratio: Double) {
|
||||
eventMap.put(HEIGHT_RATIO, ratio.toString)
|
||||
}
|
||||
}
|
||||
|
||||
object ResizeAndMoveSlideRecordEvent {
|
||||
protected final val PRES_NAME = "presentationName"
|
||||
protected final val ID = "id"
|
||||
protected final val X_CAMERA = "xCamera"
|
||||
protected final val Y_CAMERA = "yCamera"
|
||||
protected final val ZOOM = "zoom"
|
||||
protected final val X_OFFSET = "xOffset"
|
||||
protected final val Y_OFFSET = "yOffset"
|
||||
protected final val WIDTH_RATIO = "widthRatio"
|
||||
protected final val HEIGHT_RATIO = "heightRatio"
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2017 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.core.record.events
|
||||
|
||||
class TldrawCameraChangedRecordEvent extends AbstractPresentationRecordEvent {
|
||||
import TldrawCameraChangedRecordEvent._
|
||||
|
||||
setEvent("TldrawCameraChangedEvent")
|
||||
|
||||
def setPresentationName(name: String) {
|
||||
eventMap.put(PRES_NAME, name)
|
||||
}
|
||||
|
||||
def setId(id: String) {
|
||||
eventMap.put(ID, id)
|
||||
}
|
||||
|
||||
def setXCamera(xCamera: Double) {
|
||||
eventMap.put(X_CAMERA, xCamera.toString)
|
||||
}
|
||||
|
||||
def setYCamera(yCamera: Double) {
|
||||
eventMap.put(Y_CAMERA, yCamera.toString)
|
||||
}
|
||||
|
||||
def setZoom(zoom: Double) {
|
||||
eventMap.put(ZOOM, zoom.toString)
|
||||
}
|
||||
}
|
||||
|
||||
object TldrawCameraChangedRecordEvent {
|
||||
protected final val PRES_NAME = "presentationName"
|
||||
protected final val ID = "id"
|
||||
protected final val X_CAMERA = "xCamera"
|
||||
protected final val Y_CAMERA = "yCamera"
|
||||
protected final val ZOOM = "zoom"
|
||||
}
|
@ -184,7 +184,7 @@ class RedisRecorderActor(
|
||||
}
|
||||
|
||||
private def handleResizeAndMovePageEvtMsg(msg: ResizeAndMovePageEvtMsg) {
|
||||
val ev = new ResizeAndMoveSlideRecordEvent()
|
||||
val ev = new TldrawCameraChangedRecordEvent()
|
||||
ev.setMeetingId(msg.header.meetingId)
|
||||
ev.setPodId(msg.body.podId)
|
||||
ev.setPresentationName(msg.body.presentationId)
|
||||
@ -280,7 +280,7 @@ class RedisRecorderActor(
|
||||
|
||||
private def handleSendWhiteboardAnnotationsEvtMsg(msg: SendWhiteboardAnnotationsEvtMsg) {
|
||||
msg.body.annotations.foreach(annotation => {
|
||||
val ev = new AddShapeWhiteboardRecordEvent()
|
||||
val ev = new AddTldrawShapeWhiteboardRecordEvent()
|
||||
ev.setMeetingId(msg.header.meetingId)
|
||||
ev.setPresentation(getPresentationId(annotation.wbId))
|
||||
ev.setPageNumber(getPageNum(annotation.wbId))
|
||||
@ -320,7 +320,7 @@ class RedisRecorderActor(
|
||||
|
||||
private def handleDeleteWhiteboardAnnotationsEvtMsg(msg: DeleteWhiteboardAnnotationsEvtMsg) {
|
||||
msg.body.annotationsIds.foreach(annotationId => {
|
||||
val ev = new UndoAnnotationRecordEvent()
|
||||
val ev = new DeleteTldrawShapeRecordEvent()
|
||||
ev.setMeetingId(msg.header.meetingId)
|
||||
ev.setPresentation(getPresentationId(msg.body.whiteboardId))
|
||||
ev.setPageNumber(getPageNum(msg.body.whiteboardId))
|
||||
|
@ -1 +1 @@
|
||||
git clone --branch v2.9.0-alpha.3 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
||||
git clone --branch v2.9.0-alpha.4 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
||||
|
@ -1 +1 @@
|
||||
BIGBLUEBUTTON_RELEASE=2.6.0-alpha.1
|
||||
BIGBLUEBUTTON_RELEASE=2.6.0-alpha.2
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
meteor-base@1.5.1
|
||||
mobile-experience@1.1.0
|
||||
mongo@1.14.6
|
||||
mongo@1.15.0
|
||||
reactive-var@1.0.11
|
||||
|
||||
standard-minifier-css@1.8.1
|
||||
|
@ -1 +1 @@
|
||||
METEOR@2.7.1
|
||||
METEOR@2.7.3
|
||||
|
@ -1,10 +1,10 @@
|
||||
allow-deny@1.1.1
|
||||
autoupdate@1.8.0
|
||||
babel-compiler@7.9.0
|
||||
babel-runtime@1.5.0
|
||||
babel-runtime@1.5.1
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze-tools@1.1.2
|
||||
blaze-tools@1.1.3
|
||||
boilerplate-generator@1.7.1
|
||||
caching-compiler@1.2.2
|
||||
caching-html-compiler@1.2.1
|
||||
@ -25,7 +25,7 @@ es5-shim@4.8.0
|
||||
fetch@0.1.1
|
||||
geojson-utils@1.0.10
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.1.2
|
||||
html-tools@1.1.3
|
||||
htmljs@1.1.1
|
||||
http@2.0.0
|
||||
id-map@1.1.1
|
||||
@ -43,11 +43,11 @@ minifier-js@2.7.4
|
||||
minimongo@1.8.0
|
||||
mobile-experience@1.1.0
|
||||
mobile-status-bar@1.1.0
|
||||
modern-browsers@0.1.7
|
||||
modern-browsers@0.1.8
|
||||
modules@0.18.0
|
||||
modules-runtime@0.13.0
|
||||
mongo@1.14.6
|
||||
mongo-decimal@0.1.2
|
||||
mongo@1.15.0
|
||||
mongo-decimal@0.1.3
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.8
|
||||
npm-mongo@4.3.1
|
||||
@ -55,7 +55,7 @@ ordered-dict@1.1.0
|
||||
promise@0.12.0
|
||||
random@1.2.0
|
||||
react-fast-refresh@0.2.3
|
||||
react-meteor-data@2.4.0
|
||||
react-meteor-data@2.5.1
|
||||
reactive-dict@1.3.0
|
||||
reactive-var@1.0.11
|
||||
reload@1.3.1
|
||||
@ -64,12 +64,12 @@ rocketchat:streamer@1.1.0
|
||||
routepolicy@1.1.1
|
||||
session@1.2.0
|
||||
shell-server@0.5.0
|
||||
socket-stream-client@0.4.0
|
||||
spacebars-compiler@1.3.0
|
||||
socket-stream-client@0.5.0
|
||||
spacebars-compiler@1.3.1
|
||||
standard-minifier-css@1.8.1
|
||||
standard-minifier-js@2.8.0
|
||||
static-html@1.3.2
|
||||
templating-tools@1.2.1
|
||||
templating-tools@1.2.2
|
||||
tracker@1.2.0
|
||||
typescript@4.5.4
|
||||
underscore@1.0.10
|
||||
|
@ -347,6 +347,7 @@ class InputStreamLiveSelector extends Component {
|
||||
currentOutputDeviceId,
|
||||
isListenOnly,
|
||||
isRTL,
|
||||
shortcuts,
|
||||
} = this.props;
|
||||
|
||||
const inputDeviceList = !isListenOnly
|
||||
@ -379,33 +380,43 @@ class InputStreamLiveSelector extends Component {
|
||||
const dropdownListComplete = inputDeviceList.concat(outputDeviceList).concat(leaveAudioOption);
|
||||
|
||||
return (
|
||||
<BBBMenu
|
||||
trigger={(
|
||||
<>
|
||||
{isListenOnly
|
||||
? this.renderListenOnlyButton()
|
||||
: this.renderMuteToggleButton()}
|
||||
<Styled.AudioDropdown
|
||||
emoji="device_list_selector"
|
||||
label={intl.formatMessage(intlMessages.changeAudioDevice)}
|
||||
hideLabel
|
||||
tabIndex={0}
|
||||
rotate
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
actions={dropdownListComplete}
|
||||
opts={{
|
||||
id: 'default-dropdown-menu',
|
||||
keepMounted: true,
|
||||
transitionDuration: 0,
|
||||
elevation: 3,
|
||||
getContentAnchorEl: null,
|
||||
fullwidth: 'true',
|
||||
anchorOrigin: { vertical: 'top', horizontal: isRTL ? 'left' : 'right' },
|
||||
transformOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' },
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
{!isListenOnly ? (
|
||||
<span
|
||||
style={{ display: 'none' }}
|
||||
accessKey={shortcuts.leaveaudio}
|
||||
onClick={() => handleLeaveAudio()}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : null}
|
||||
<BBBMenu
|
||||
trigger={(
|
||||
<>
|
||||
{isListenOnly
|
||||
? this.renderListenOnlyButton()
|
||||
: this.renderMuteToggleButton()}
|
||||
<Styled.AudioDropdown
|
||||
emoji="device_list_selector"
|
||||
label={intl.formatMessage(intlMessages.changeAudioDevice)}
|
||||
hideLabel
|
||||
tabIndex={0}
|
||||
rotate
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
actions={dropdownListComplete}
|
||||
opts={{
|
||||
id: 'default-dropdown-menu',
|
||||
keepMounted: true,
|
||||
transitionDuration: 0,
|
||||
elevation: 3,
|
||||
getContentAnchorEl: null,
|
||||
fullwidth: 'true',
|
||||
anchorOrigin: { vertical: 'top', horizontal: isRTL ? 'left' : 'right' },
|
||||
transformOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' },
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -434,4 +445,4 @@ InputStreamLiveSelector.propTypes = propTypes;
|
||||
InputStreamLiveSelector.defaultProps = defaultProps;
|
||||
|
||||
export default withShortcutHelper(injectIntl(InputStreamLiveSelector),
|
||||
['leaveAudio']);
|
||||
['leaveAudio', 'toggleMute']);
|
||||
|
@ -24,6 +24,8 @@ const propTypes = {
|
||||
hideLabel: PropTypes.bool,
|
||||
|
||||
className: PropTypes.string,
|
||||
|
||||
rotate: PropTypes.bool,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@ -35,6 +37,7 @@ const defaultProps = {
|
||||
hideLabel: false,
|
||||
onClick: null,
|
||||
className: '',
|
||||
rotate: false,
|
||||
};
|
||||
|
||||
const ButtonEmoji = (props) => {
|
||||
@ -42,6 +45,7 @@ const ButtonEmoji = (props) => {
|
||||
hideLabel,
|
||||
className,
|
||||
hidden,
|
||||
rotate,
|
||||
...newProps
|
||||
} = props;
|
||||
|
||||
@ -52,7 +56,7 @@ const ButtonEmoji = (props) => {
|
||||
onClick,
|
||||
} = newProps;
|
||||
|
||||
const IconComponent = (<Styled.EmojiButtonIcon iconName={emoji} />);
|
||||
const IconComponent = (<Styled.EmojiButtonIcon iconName={emoji} rotate={rotate} />);
|
||||
|
||||
return (
|
||||
<span>
|
||||
|
@ -65,15 +65,6 @@ const EmojiButton = styled.button`
|
||||
margin-top: 40%;
|
||||
color: ${btnDefaultColor};
|
||||
}
|
||||
|
||||
${({ rotate }) => rotate && `
|
||||
span {
|
||||
i {
|
||||
transform: rotate(180deg);
|
||||
margin-top: 20%;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const EmojiButtonSpace = styled.div`
|
||||
|
@ -2,26 +2,31 @@ import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cx from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import Styled from './styles';
|
||||
|
||||
const propTypes = {
|
||||
iconName: PropTypes.string.isRequired,
|
||||
prependIconName: PropTypes.string,
|
||||
rotate: PropTypes.bool,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
prependIconName: 'icon-bbb-',
|
||||
rotate: false,
|
||||
};
|
||||
|
||||
const Icon = ({
|
||||
className,
|
||||
prependIconName,
|
||||
iconName,
|
||||
rotate,
|
||||
...props
|
||||
}) => (
|
||||
<i
|
||||
<Styled.Icon
|
||||
className={cx(className, [prependIconName, iconName].join(''))}
|
||||
// ToastContainer from react-toastify passes a useless closeToast prop here
|
||||
{..._.omit(props, ['closeToast', 'animations', 'rotate'])}
|
||||
{..._.omit(props, ['closeToast', 'animations'])}
|
||||
$rotate={rotate}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Icon = styled.i`
|
||||
${({ $rotate }) => $rotate && `
|
||||
transform: rotate(180deg);
|
||||
margin-top: 20%;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default {
|
||||
Icon,
|
||||
};
|
@ -80,7 +80,7 @@ const PositionLabel = (props) => {
|
||||
isMultiUserActive,
|
||||
} = props;
|
||||
|
||||
const { name, color, userId, presenter } = currentUser;
|
||||
const { name, color } = currentUser;
|
||||
const prevCurrentPoint = usePrevious(currentPoint);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -127,8 +127,8 @@ export default function Cursors(props) {
|
||||
|
||||
const end = () => {
|
||||
publishCursorUpdate({
|
||||
xPercent: null,
|
||||
yPercent: null,
|
||||
xPercent: -1.0,
|
||||
yPercent: -1.0,
|
||||
whiteboardId: whiteboardId,
|
||||
});
|
||||
setActive(false);
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
MODELS,
|
||||
getVirtualBgImagePath,
|
||||
} from '/imports/ui/services/virtual-background/service'
|
||||
import logger from '/imports/startup/client/logger';
|
||||
|
||||
const blurValue = '25px';
|
||||
|
||||
@ -195,9 +196,23 @@ class VirtualBackgroundService {
|
||||
* @returns {void}
|
||||
*/
|
||||
_renderMask() {
|
||||
this.resizeSource();
|
||||
this.runInference();
|
||||
this.runPostProcessing();
|
||||
try {
|
||||
this.resizeSource();
|
||||
this.runInference();
|
||||
this.runPostProcessing();
|
||||
} catch (error) {
|
||||
// TODO This is a high frequency log so that's why it's debug level.
|
||||
// Should be reviewed later when the actual problem with runPostProcessing
|
||||
// throwing on stalled pages/iframes - prlanzarin Jun 30 2022
|
||||
logger.debug({
|
||||
logCode: 'virtualbg_renderMask_failure',
|
||||
extraInfo: {
|
||||
errorMessage: error.message,
|
||||
errorCode: error.code,
|
||||
errorName: error.name,
|
||||
},
|
||||
}, `Virtual background renderMask failed: ${error.message || error.name}`);
|
||||
}
|
||||
|
||||
this._maskFrameTimerWorker.postMessage({
|
||||
id: SET_TIMEOUT,
|
||||
|
41
bigbluebutton-html5/package-lock.json
generated
41
bigbluebutton-html5/package-lock.json
generated
@ -3595,24 +3595,6 @@
|
||||
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
|
||||
"dev": true
|
||||
},
|
||||
"is-extendable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
|
||||
"integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
|
||||
"requires": {
|
||||
"is-plain-object": "^2.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"requires": {
|
||||
"isobject": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@ -3733,11 +3715,6 @@
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
@ -5337,14 +5314,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"object.omit": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz",
|
||||
"integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==",
|
||||
"requires": {
|
||||
"is-extendable": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"object.values": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz",
|
||||
@ -5913,16 +5882,6 @@
|
||||
"tinycolor2": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"react-cursor-position": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-cursor-position/-/react-cursor-position-3.0.3.tgz",
|
||||
"integrity": "sha512-jrmHFKQtfNdvfJ5hIH+FOb2h9+mgcD8/POOY7LngmsYCJNlg6IYdnGdbMGMTeyue/iUvY+t20t20RDrH+qW5dw==",
|
||||
"requires": {
|
||||
"object-assign": "^4.1.1",
|
||||
"object.omit": "^3.0.0",
|
||||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
|
@ -59,7 +59,7 @@ source /etc/lsb-release
|
||||
|
||||
# Set up specific version of node
|
||||
if [ "$DISTRIB_CODENAME" == "focal" ]; then
|
||||
node_version="14.19.1"
|
||||
node_version="14.19.3"
|
||||
if [[ ! -d /usr/share/node-v${node_version}-linux-x64 ]]; then
|
||||
cd /usr/share
|
||||
tar xfz "node-v${node_version}-linux-x64.tar.gz"
|
||||
|
@ -92,11 +92,11 @@ cp bbb-html5-frontend@.service staging/usr/lib/systemd/system
|
||||
|
||||
mkdir -p staging/usr/share
|
||||
|
||||
if [ ! -f node-v14.19.1-linux-x64.tar.gz ]; then
|
||||
wget https://nodejs.org/dist/v14.19.1/node-v14.19.1-linux-x64.tar.gz
|
||||
if [ ! -f node-v14.19.3-linux-x64.tar.gz ]; then
|
||||
wget https://nodejs.org/dist/v14.19.3/node-v14.19.3-linux-x64.tar.gz
|
||||
fi
|
||||
|
||||
cp node-v14.19.1-linux-x64.tar.gz staging/usr/share
|
||||
cp node-v14.19.3-linux-x64.tar.gz staging/usr/share
|
||||
|
||||
if [ -f staging/usr/share/meteor/bundle/programs/web.browser/head.html ]; then
|
||||
sed -i "s/VERSION/$(($BUILD))/" staging/usr/share/meteor/bundle/programs/web.browser/head.html
|
||||
|
@ -49,7 +49,7 @@ fi
|
||||
export MONGO_OPLOG_URL=mongodb://127.0.1.1/local
|
||||
export MONGO_URL=mongodb://127.0.1.1/meteor
|
||||
export NODE_ENV=production
|
||||
export NODE_VERSION=node-v14.19.1-linux-x64
|
||||
export NODE_VERSION=node-v14.19.3-linux-x64
|
||||
export SERVER_WEBSOCKET_COMPRESSION=0
|
||||
export BIND_IP=127.0.0.1
|
||||
PORT=$PORT /usr/share/$NODE_VERSION/bin/node --max-old-space-size=2048 --max_semi_space_size=128 main.js NODEJS_BACKEND_INSTANCE_ID=$INSTANCE_ID
|
||||
|
@ -49,7 +49,7 @@ fi
|
||||
export MONGO_OPLOG_URL=mongodb://127.0.1.1/local
|
||||
export MONGO_URL=mongodb://127.0.1.1/meteor
|
||||
export NODE_ENV=production
|
||||
export NODE_VERSION=node-v14.19.1-linux-x64
|
||||
export NODE_VERSION=node-v14.19.3-linux-x64
|
||||
export SERVER_WEBSOCKET_COMPRESSION=0
|
||||
export BIND_IP=127.0.0.1
|
||||
PORT=$PORT /usr/share/$NODE_VERSION/bin/node --max-old-space-size=2048 --max_semi_space_size=128 main.js
|
||||
|
@ -960,7 +960,7 @@ module BigBlueButton
|
||||
# The following events are considered to indicate that the presentation
|
||||
# area was actively used during the session.
|
||||
when 'AddShapeEvent', 'ModifyTextEvent', 'UndoShapeEvent',
|
||||
'ClearPageEvent'
|
||||
'ClearPageEvent', 'AddTldrawShapeEvent', 'DeleteTldrawShapeEvent'
|
||||
BigBlueButton.logger.debug("Seen a #{event['eventname']} event, presentation area used.")
|
||||
return true
|
||||
# We ignore the first SharePresentationEvent, since it's the default
|
||||
@ -1093,5 +1093,11 @@ module BigBlueButton
|
||||
return false
|
||||
end
|
||||
|
||||
# Check if doc has tldraw events
|
||||
def self.check_for_tldraw_events(events)
|
||||
return !(events.xpath("recording/event[@eventname='TldrawCameraChangedEvent']").empty? &&
|
||||
events.xpath("recording/event[@eventname='AddTldrawShapeEvent']").empty?)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -192,6 +192,10 @@ unless FileTest.directory?(target_dir)
|
||||
|
||||
# Copy thumbnails from raw files
|
||||
FileUtils.cp_r("#{pres_dir}/thumbnails", "#{target_pres_dir}/thumbnails") if File.exist?("#{pres_dir}/thumbnails")
|
||||
tldraw = BigBlueButton::Events.check_for_tldraw_events(@doc);
|
||||
if (tldraw)
|
||||
FileUtils.cp_r("#{pres_dir}/svgs", "#{target_pres_dir}/svgs") if File.exist?("#{pres_dir}/svgs")
|
||||
end
|
||||
end
|
||||
|
||||
BigBlueButton.logger.info('Generating closed captions')
|
||||
|
@ -382,6 +382,30 @@ def svg_render_shape_poll(g, slide, shape)
|
||||
width: width, height: height, x: x, y: y)
|
||||
end
|
||||
|
||||
def build_tldraw_shape(image_shapes, slide, shape)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
tldraw_shape = {
|
||||
id: shape[:shape_id],
|
||||
timestamp: shape_in,
|
||||
undo: (shape[:undo].nil? ? -1 : shape[:undo]),
|
||||
shape_data: shape[:shape_data]
|
||||
}
|
||||
|
||||
image_shapes.push(tldraw_shape)
|
||||
end
|
||||
|
||||
def svg_render_shape(canvas, slide, shape, image_id)
|
||||
shape_in = shape[:in]
|
||||
shape_out = shape[:out]
|
||||
@ -426,7 +450,7 @@ def svg_render_shape(canvas, slide, shape, image_id)
|
||||
end
|
||||
|
||||
@svg_image_id = 1
|
||||
def svg_render_image(svg, slide, shapes)
|
||||
def svg_render_image(svg, slide, shapes, tldraw, tldraw_shapes)
|
||||
slide_number = slide[:slide]
|
||||
presentation = slide[:presentation]
|
||||
slide_in = slide[:in]
|
||||
@ -458,15 +482,25 @@ def svg_render_image(svg, slide, shapes)
|
||||
|
||||
shapes = shapes[presentation][slide_number]
|
||||
|
||||
canvas = doc.create_element('g',
|
||||
class: 'canvas', id: "canvas#{image_id}",
|
||||
image: "image#{image_id}", display: 'none')
|
||||
if !tldraw
|
||||
canvas = doc.create_element('g',
|
||||
class: 'canvas', id: "canvas#{image_id}",
|
||||
image: "image#{image_id}", display: 'none')
|
||||
|
||||
shapes.each do |shape|
|
||||
svg_render_shape(canvas, slide, shape, image_id)
|
||||
shapes.each do |shape|
|
||||
svg_render_shape(canvas, slide, shape, image_id)
|
||||
end
|
||||
|
||||
svg << canvas unless canvas.element_children.empty?
|
||||
else
|
||||
image_shapes = []
|
||||
|
||||
shapes.each do |shape|
|
||||
build_tldraw_shape(image_shapes, slide, shape)
|
||||
end
|
||||
|
||||
tldraw_shapes[image_id] = { :shapes=>image_shapes, :timestamp=> slide_in}
|
||||
end
|
||||
|
||||
svg << canvas unless canvas.element_children.empty?
|
||||
end
|
||||
|
||||
def panzoom_viewbox(panzoom)
|
||||
@ -483,13 +517,19 @@ def panzoom_viewbox(panzoom)
|
||||
[x, y, w, h]
|
||||
end
|
||||
|
||||
def panzooms_emit_event(rec, panzoom)
|
||||
def panzooms_emit_event(rec, panzoom, tldraw)
|
||||
panzoom_in = panzoom[:in]
|
||||
return if panzoom_in == panzoom[:out]
|
||||
|
||||
rec.event(timestamp: panzoom_in) do
|
||||
x, y, w, h = panzoom_viewbox(panzoom)
|
||||
rec.viewBox("#{x} #{y} #{w} #{h}")
|
||||
if !tldraw
|
||||
rec.event(timestamp: panzoom_in) do
|
||||
x, y, w, h = panzoom_viewbox(panzoom)
|
||||
rec.viewBox("#{x} #{y} #{w} #{h}")
|
||||
end
|
||||
else
|
||||
rec.event(timestamp: panzoom_in) do
|
||||
rec.cameraAndZoom("#{panzoom[:x_camera]} #{panzoom[:y_camera]} #{panzoom[:zoom]}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -497,14 +537,14 @@ 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)
|
||||
def cursors_emit_event(rec, cursor, tldraw)
|
||||
cursor_in = cursor[:in]
|
||||
return if cursor_in == cursor[:out]
|
||||
|
||||
rec.event(timestamp: cursor_in) do
|
||||
panzoom = cursor[:panzoom]
|
||||
if cursor[:visible]
|
||||
if @version_atleast_2_0_0
|
||||
if @version_atleast_2_0_0 && !tldraw
|
||||
# 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 = convert_cursor_coordinate(cursor[:x], panzoom[:x_offset], panzoom[:width_ratio])
|
||||
@ -535,6 +575,53 @@ def determine_slide_number(slide, current_slide)
|
||||
slide
|
||||
end
|
||||
|
||||
def events_parse_tldraw_shape(shapes, event, current_presentation, current_slide, timestamp)
|
||||
presentation = event.at_xpath('presentation')
|
||||
slide = event.at_xpath('pageNumber')
|
||||
|
||||
presentation = determine_presentation(presentation, current_presentation)
|
||||
slide = determine_slide_number(slide, current_slide)
|
||||
|
||||
# Set up the shapes data structures if needed
|
||||
shapes[presentation] ||= {}
|
||||
shapes[presentation][slide] ||= []
|
||||
|
||||
# We only need to deal with shapes for this slide
|
||||
shapes = shapes[presentation][slide]
|
||||
|
||||
# Set up the structure for this shape
|
||||
shape = {}
|
||||
# Common properties
|
||||
shape[:in] = timestamp
|
||||
shape_data = shape[:shape_data] = JSON.parse(event.at_xpath('shapeData'))
|
||||
|
||||
user_id = event.at_xpath('userId')&.text
|
||||
shape[:user_id] = user_id if user_id
|
||||
|
||||
shape_id = event.at_xpath('shapeId')&.text
|
||||
shape[:id] = shape_id if shape_id
|
||||
|
||||
draw_id = shape[:shape_id] = @svg_shape_id
|
||||
@svg_shape_id += 1
|
||||
|
||||
# Find the previous shape, for updates
|
||||
prev_shape = nil
|
||||
if shape_id
|
||||
# If we have a shape ID, look up the previous shape by ID
|
||||
prev_shape_pos = shapes.rindex { |s| s[:shade_id] == shape_id }
|
||||
prev_shape = prev_shape_pos ? shapes[prev_shape_pos] : nil
|
||||
end
|
||||
if prev_shape
|
||||
prev_shape[:out] = timestamp
|
||||
shape[:shape_unique_id] = prev_shape[:shape_unique_id]
|
||||
else
|
||||
shape[:shape_unique_id] = @svg_shape_unique_id
|
||||
@svg_shape_unique_id += 1
|
||||
end
|
||||
|
||||
shapes << shape
|
||||
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
|
||||
@ -734,7 +821,7 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times
|
||||
end
|
||||
end
|
||||
|
||||
def events_get_image_info(slide)
|
||||
def events_get_image_info(slide, tldraw)
|
||||
slide_deskshare = slide[:deskshare]
|
||||
slide_presentation = slide[:presentation]
|
||||
|
||||
@ -744,7 +831,8 @@ def events_get_image_info(slide)
|
||||
slide[:src] = 'presentation/logo.png'
|
||||
else
|
||||
slide_nr = slide[:slide] + 1
|
||||
slide[:src] = "presentation/#{slide_presentation}/slide-#{slide_nr}.png"
|
||||
tldraw ? slide[:src] = "presentation/#{slide_presentation}/svgs/slide#{slide_nr}.svg"
|
||||
: 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]}"
|
||||
@ -792,6 +880,7 @@ def process_presentation(package_dir)
|
||||
# Current pan/zoom state
|
||||
current_x_offset = current_y_offset = 0.0
|
||||
current_width_ratio = current_height_ratio = 100.0
|
||||
current_x_camera = current_y_camera = current_zoom = 0.0
|
||||
# Current cursor status
|
||||
cursor_x = cursor_y = -1.0
|
||||
cursor_visible = false
|
||||
@ -802,6 +891,8 @@ def process_presentation(package_dir)
|
||||
panzooms = []
|
||||
cursors = []
|
||||
shapes = {}
|
||||
tldraw = BigBlueButton::Events.check_for_tldraw_events(@doc)
|
||||
tldraw_shapes = {}
|
||||
|
||||
# Iterate through the events.xml and store the events, building the
|
||||
# xml files as we go
|
||||
@ -836,6 +927,12 @@ def process_presentation(package_dir)
|
||||
current_height_ratio = event.at_xpath('heightRatio').text.to_f
|
||||
panzoom_changed = true
|
||||
|
||||
when 'TldrawCameraChangedEvent'
|
||||
current_x_camera = event.at_xpath('xCamera').text.to_f
|
||||
current_y_camera = event.at_xpath('yCamera').text.to_f
|
||||
current_zoom = event.at_xpath('zoom').text.to_f
|
||||
panzoom_changed = true
|
||||
|
||||
when 'DeskshareStartedEvent', 'StartWebRTCDesktopShareEvent'
|
||||
deskshare = slide_changed = true if @presentation_props['include_deskshare']
|
||||
|
||||
@ -848,7 +945,10 @@ def process_presentation(package_dir)
|
||||
when 'AddShapeEvent', 'ModifyTextEvent'
|
||||
events_parse_shape(shapes, event, current_presentation, current_slide, timestamp)
|
||||
|
||||
when 'UndoShapeEvent', 'UndoAnnotationEvent'
|
||||
when 'AddTldrawShapeEvent'
|
||||
events_parse_tldraw_shape(shapes, event, current_presentation, current_slide, timestamp)
|
||||
|
||||
when 'UndoShapeEvent', 'UndoAnnotationEvent', 'DeleteTldrawShapeEvent'
|
||||
events_parse_undo(shapes, event, current_presentation, current_slide, timestamp)
|
||||
|
||||
when 'ClearPageEvent', 'ClearWhiteboardEvent'
|
||||
@ -887,7 +987,7 @@ def process_presentation(package_dir)
|
||||
else
|
||||
if slide
|
||||
slide[:out] = timestamp
|
||||
svg_render_image(svg, slide, shapes)
|
||||
svg_render_image(svg, slide, shapes, tldraw, tldraw_shapes)
|
||||
end
|
||||
|
||||
BigBlueButton.logger.info("Presentation #{current_presentation} Slide #{current_slide} Deskshare #{deskshare}")
|
||||
@ -897,7 +997,7 @@ def process_presentation(package_dir)
|
||||
in: timestamp,
|
||||
deskshare: deskshare,
|
||||
}
|
||||
events_get_image_info(slide)
|
||||
events_get_image_info(slide, tldraw)
|
||||
slides << slide
|
||||
end
|
||||
end
|
||||
@ -909,40 +1009,59 @@ def process_presentation(package_dir)
|
||||
slide_width = slide[:width]
|
||||
slide_height = slide[:height]
|
||||
if panzoom &&
|
||||
(panzoom[:deskshare] == deskshare) &&
|
||||
((!tldraw &&
|
||||
(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)
|
||||
(panzoom[:height] == slide_height)) ||
|
||||
(tldraw &&
|
||||
(panzoom[:x_camera] == current_x_camera) &&
|
||||
(panzoom[:y_camera] == current_y_camera) &&
|
||||
(panzoom[:zoom] == current_zoom))
|
||||
)
|
||||
BigBlueButton.logger.info('Panzoom: skipping, no changes')
|
||||
panzoom_changed = false
|
||||
else
|
||||
if panzoom
|
||||
panzoom[:out] = timestamp
|
||||
panzooms_emit_event(panzooms_rec, panzoom)
|
||||
panzooms_emit_event(panzooms_rec, panzoom, tldraw)
|
||||
end
|
||||
if !tldraw
|
||||
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,
|
||||
width_ratio: current_width_ratio,
|
||||
height_ratio: current_height_ratio,
|
||||
width: slide[:width],
|
||||
height: slide[:height],
|
||||
in: timestamp,
|
||||
deskshare: deskshare,
|
||||
}
|
||||
else
|
||||
BigBlueButton.logger.info("Panzoom: #{current_x_camera} #{current_y_camera} #{current_zoom} (#{slide_width}x#{slide_height})")
|
||||
panzoom = {
|
||||
x_camera: current_x_camera,
|
||||
y_camera: current_y_camera,
|
||||
zoom: current_zoom,
|
||||
in: timestamp,
|
||||
deskshare: deskshare,
|
||||
}
|
||||
end
|
||||
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,
|
||||
width_ratio: current_width_ratio,
|
||||
height_ratio: current_height_ratio,
|
||||
width: slide[:width],
|
||||
height: slide[:height],
|
||||
in: timestamp,
|
||||
deskshare: deskshare,
|
||||
}
|
||||
panzooms << panzoom
|
||||
end
|
||||
end
|
||||
|
||||
# Perform cursor finalization
|
||||
if cursor_changed || panzoom_changed
|
||||
unless cursor_x >= 0 && cursor_x <= 100 &&
|
||||
cursor_y >= 0 && cursor_y <= 100
|
||||
cursor_visible = false
|
||||
if !tldraw
|
||||
unless cursor_x >= 0 && cursor_x <= 100 &&
|
||||
cursor_y >= 0 && cursor_y <= 100
|
||||
cursor_visible = false
|
||||
end
|
||||
end
|
||||
|
||||
panzoom = panzooms.last
|
||||
@ -955,7 +1074,7 @@ def process_presentation(package_dir)
|
||||
else
|
||||
if cursor
|
||||
cursor[:out] = timestamp
|
||||
cursors_emit_event(cursors_rec, cursor)
|
||||
cursors_emit_event(cursors_rec, cursor, tldraw)
|
||||
end
|
||||
cursor = {
|
||||
visible: cursor_visible,
|
||||
@ -972,26 +1091,27 @@ def process_presentation(package_dir)
|
||||
# Add the last slide, panzoom, and cursor
|
||||
slide = slides.last
|
||||
slide[:out] = last_timestamp
|
||||
svg_render_image(svg, slide, shapes)
|
||||
svg_render_image(svg, slide, shapes, tldraw, tldraw_shapes)
|
||||
panzoom = panzooms.last
|
||||
panzoom[:out] = last_timestamp
|
||||
panzooms_emit_event(panzooms_rec, panzoom)
|
||||
panzooms_emit_event(panzooms_rec, panzoom, tldraw)
|
||||
cursor = cursors.last
|
||||
cursor[:out] = last_timestamp
|
||||
cursors_emit_event(cursors_rec, cursor)
|
||||
cursors_emit_event(cursors_rec, cursor, tldraw)
|
||||
|
||||
cursors_doc = Builder::XmlMarkup.new(indent: 2)
|
||||
cursors_doc.instruct!
|
||||
cursors_doc.recording(id: 'cursor_events') { |xml| xml << cursors_rec.target! }
|
||||
cursors_doc.recording(id: 'cursor_events', tldraw: tldraw) { |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! }
|
||||
panzooms_doc.recording(id: 'panzoom_events', tldraw: tldraw) { |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.target!)
|
||||
File.write("#{package_dir}/#{@cursor_xml_filename}", cursors_doc.target!)
|
||||
generate_json_file(package_dir, @tldraw_shapes_filename, tldraw_shapes) if tldraw
|
||||
end
|
||||
|
||||
def process_chat_messages(events, bbb_props)
|
||||
@ -1170,6 +1290,7 @@ end
|
||||
@panzooms_xml_filename = 'panzooms.xml'
|
||||
@cursor_xml_filename = 'cursor.xml'
|
||||
@deskshare_xml_filename = 'deskshare.xml'
|
||||
@tldraw_shapes_filename = 'tldraw.json'
|
||||
@svg_shape_id = 1
|
||||
@svg_shape_unique_id = 1
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user