Merge pull request #3982 from ritzalam/move-java-classes-from-bbb-web-to-bbb-common-web
Merging new way of sending messages
This commit is contained in:
commit
61a1734efb
543
DEVELOPMENT.md
Normal file → Executable file
543
DEVELOPMENT.md
Normal file → Executable file
@ -1,371 +1,27 @@
|
||||
This document provides instructions for developers to setup their
|
||||
environment and work on the upcoming BBB 1.1 (tentative release version).
|
||||
environment and work on the upcoming BBB 2.0 (tentative release version).
|
||||
|
||||
## Install BBB 1.0
|
||||
## Install BBB 1.1
|
||||
|
||||
Make sure you have a working BBB 1.0 before you proceed with the instructions below.
|
||||
Follow the (install instructions)[http://docs.bigbluebutton.org/install/install.html] for 1.1.
|
||||
|
||||
## Install OpenJDK 8
|
||||
Make sure you have a working BBB 1.1 before you proceed with the instructions below.
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:openjdk-r/ppa
|
||||
sudo apt-get update
|
||||
sudo apt-get install openjdk-8-jdk
|
||||
```
|
||||
## Setup development environment
|
||||
|
||||
Change the default jre. Choose Java 8.
|
||||
Setup your development environment following these (instructions)[http://docs.bigbluebutton.org/dev/setup.html]
|
||||
|
||||
```
|
||||
sudo update-alternatives --config java
|
||||
```
|
||||
## Checkout development branch
|
||||
|
||||
Change the default jdk. Choose Jdk8
|
||||
Checkout the development branch `move-java-classes-from-bbb-web-to-bbb-common-web` from this (repository)[https://github.com/ritzalam/bigbluebutton]
|
||||
|
||||
```
|
||||
sudo update-alternatives --config javac
|
||||
```
|
||||
Open nine (9) terminal windows so you will dedicate one window for each bbb-component.
|
||||
You can name them client, bbb-apps, apps-common, red5, akka-apps, akka-fsesl, bbb-web, common-web, and messages.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Edit `~/.profile` and change `JAVA_HOME`
|
||||
|
||||
```
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
|
||||
```
|
||||
|
||||
Save the file and refresh environment vars.
|
||||
|
||||
```
|
||||
source ~/.profile
|
||||
```
|
||||
|
||||
## Update Development Tools
|
||||
|
||||
### Install The Core Development Tools
|
||||
|
||||
```
|
||||
sudo apt-get install git-core ant
|
||||
```
|
||||
|
||||
|
||||
### Install Gradle
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget http://services.gradle.org/distributions/gradle-2.12-bin.zip
|
||||
unzip gradle-2.12-bin.zip
|
||||
ln -s gradle-2.12 gradle
|
||||
```
|
||||
|
||||
### Install Grails
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget https://github.com/grails/grails-core/releases/download/v2.5.2/grails-2.5.2.zip
|
||||
unzip grails-2.5.2.zip
|
||||
ln -s grails-2.5.2 grails
|
||||
```
|
||||
|
||||
### Install Maven
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget apache.parentingamerica.com//maven/maven-3/3.3.3/binaries/apache-maven-3.3.3-bin.zip
|
||||
unzip apache-maven-3.3.3-bin.zip
|
||||
ln -s apache-maven-3.3.3 maven
|
||||
```
|
||||
|
||||
### Install sbt
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget https://dl.bintray.com/sbt/native-packages/sbt/0.13.9/sbt-0.13.9.tgz
|
||||
tar zxvf sbt-0.13.9.tgz
|
||||
```
|
||||
|
||||
In the next step, you need to get the Apache Flex 4.13.0 SDK package.
|
||||
|
||||
**Note:** Even though we're downloading the Apache Flex 4.13.0 SDK, BigBlueButton is developed and built with Flex 3 compatibility mode enabled.
|
||||
|
||||
First, you need to download the SDK tarball from an Apache mirror site and then unpack it.
|
||||
|
||||
```
|
||||
wget https://archive.apache.org/dist/flex/4.13.0/binaries/apache-flex-sdk-4.13.0-bin.tar.gz
|
||||
tar xvfz apache-flex-sdk-4.13.0-bin.tar.gz
|
||||
```
|
||||
|
||||
Once Flex SDK is unpacked, you need to download the Adobe Flex SDK. We'll do this step manually in case the download fails (if it does, remove the incomplete file and issue the `wget` command again).
|
||||
|
||||
```
|
||||
cd apache-flex-sdk-4.13.0-bin/
|
||||
mkdir -p in/
|
||||
wget http://download.macromedia.com/pub/flex/sdk/builds/flex4.6/flex_sdk_4.6.0.23201B.zip -P in/
|
||||
```
|
||||
|
||||
Once the SDK has downloaded, we can use its `build.xml` script to automatically download the remaining third-party tools.
|
||||
|
||||
```
|
||||
ant -f frameworks/build.xml thirdparty-downloads
|
||||
```
|
||||
|
||||
After Flex downloads the remaining third-party tools, you need to modify their permissions.
|
||||
|
||||
```
|
||||
sudo find ~/dev/tools/apache-flex-sdk-4.13.0-bin -type d -exec chmod o+rx '{}' \;
|
||||
chmod 755 ~/dev/tools/apache-flex-sdk-4.13.0-bin/bin/*
|
||||
sudo chmod -R +r ~/dev/tools/apache-flex-sdk-4.13.0-bin
|
||||
```
|
||||
|
||||
Next, create a linked directory with a shortened name for easier referencing.
|
||||
|
||||
```
|
||||
ln -s ~/dev/tools/apache-flex-sdk-4.13.0-bin ~/dev/tools/flex
|
||||
```
|
||||
|
||||
The next step in setting up the Flex SDK environment is to download a Flex library for video.
|
||||
|
||||
```
|
||||
cd ~/dev/tools/
|
||||
mkdir -p apache-flex-sdk-4.13.0-bin/frameworks/libs/player/11.2
|
||||
cd apache-flex-sdk-4.13.0-bin/frameworks/libs/player/11.2
|
||||
wget http://fpdownload.macromedia.com/get/flashplayer/installers/archive/playerglobal/playerglobal11_2.swc
|
||||
mv -f playerglobal11_2.swc playerglobal.swc
|
||||
```
|
||||
|
||||
The last step to have a working Flex SDK is to configure it to work with playerglobal 11.2
|
||||
|
||||
```
|
||||
cd ~/dev/tools/apache-flex-sdk-4.13.0-bin
|
||||
sudo sed -i "s/11.1/11.2/g" frameworks/flex-config.xml
|
||||
sudo sed -i "s/<swf-version>14<\/swf-version>/<swf-version>15<\/swf-version>/g" frameworks/flex-config.xml
|
||||
sudo sed -i "s/{playerglobalHome}\/{targetPlayerMajorVersion}.{targetPlayerMinorVersion}/libs\/player\/11.2/g" frameworks/flex-config.xml
|
||||
```
|
||||
|
||||
With the tools installed, you need to add a set of environment variables to your `.profile` to access these tools.
|
||||
|
||||
```
|
||||
vi ~/.profile
|
||||
```
|
||||
|
||||
Copy-and-paste the following text at bottom of `.profile`.
|
||||
|
||||
```
|
||||
|
||||
export GRAILS_HOME=$HOME/dev/tools/grails
|
||||
export PATH=$PATH:$GRAILS_HOME/bin
|
||||
|
||||
export FLEX_HOME=$HOME/dev/tools/flex
|
||||
export PATH=$PATH:$FLEX_HOME/bin
|
||||
|
||||
export GRADLE_HOME=$HOME/dev/tools/gradle
|
||||
export PATH=$PATH:$GRADLE_HOME/bin
|
||||
|
||||
export SBT_HOME=$HOME/dev/tools/sbt
|
||||
export PATH=$PATH:$SBT_HOME/bin
|
||||
|
||||
export MAVEN_HOME=$HOME/dev/tools/maven
|
||||
export PATH=$PATH:$MAVEN_HOME/bin
|
||||
|
||||
|
||||
```
|
||||
|
||||
Reload your profile to use these tools (this will happen automatically when you next login).
|
||||
|
||||
```
|
||||
source ~/.profile
|
||||
```
|
||||
|
||||
Check that the tools are now in your path by running the following command.
|
||||
|
||||
```
|
||||
$ mxmlc -version
|
||||
Version 4.13.0 build 20140701
|
||||
```
|
||||
|
||||
## Setup Red5
|
||||
|
||||
```
|
||||
cd /usr/share
|
||||
|
||||
# Make a backup of the deployed red5
|
||||
|
||||
sudo mv red5 red5-orig
|
||||
|
||||
# Symlink red5 to old red5
|
||||
|
||||
sudo ln -s red5-orig red5
|
||||
```
|
||||
|
||||
## Build Red5
|
||||
|
||||
Build red5-parent
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-parent.git
|
||||
cd red5-parent/
|
||||
git checkout snapshot-mar-30-2016
|
||||
mvn install
|
||||
```
|
||||
|
||||
Build red5-io
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-io.git
|
||||
cd red5-io
|
||||
git checkout snapshot-mar-30-2016
|
||||
./bbb-build.sh
|
||||
```
|
||||
|
||||
Build red5-server-common
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-server-common.git
|
||||
cd red5-server-common
|
||||
git checkout snapshot-mar-30-2016
|
||||
./bbb-build.sh
|
||||
```
|
||||
|
||||
Build red5-server
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-server.git
|
||||
cd red5-server
|
||||
git checkout snapshot-mar-30-2016
|
||||
./build-red5.sh
|
||||
|
||||
# Deploy red5, this will copy the new red5 to /usr/share
|
||||
# and modify the symlink you created above.
|
||||
|
||||
./deploy-red5.sh
|
||||
```
|
||||
|
||||
# Developing the client
|
||||
|
||||
|
||||
# Client Development
|
||||
With the development environment checked out and the code cloned, we are ready to start developing!
|
||||
|
||||
This section will walk you through making a simple change to the BigBlueButton client.
|
||||
|
||||
## Setting up the environment
|
||||
|
||||
The first thing you need to do is to copy the template `config.xml` file to the build directory for the client.
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/
|
||||
cp bigbluebutton-client/resources/config.xml.template bigbluebutton-client/src/conf/config.xml
|
||||
```
|
||||
|
||||
The `config.xml` file is one of the first files loaded by the BigBlueButton client when it connects to the server. The `config.xml` file tells BigBlueButton client how to load the remaining components (such as chat module, deskshare module, video conf module, etc.) and sets a number of configuration parameters for each component. The `config.xml` specifies the hostname (or IP address) for loading each component.
|
||||
|
||||
Let's look at the first ten lines of the `config.xml` file you just copied.
|
||||
|
||||
```
|
||||
$ head -n 10 bigbluebutton-client/src/conf/config.xml
|
||||
<?xml version="1.0" ?>
|
||||
<config>
|
||||
<localeversion suppressWarning="false">0.9.0</localeversion>
|
||||
<version>VERSION</version>
|
||||
<help url="http://HOST/help.html"/>
|
||||
<javaTest url="http://HOST/testjava.html"/>
|
||||
<porttest host="HOST" application="video/portTest" timeout="10000"/>
|
||||
<bwMon server="HOST" application="video/bwTest"/>
|
||||
<application uri="rtmp://HOST/bigbluebutton" host="http://HOST/bigbluebutton/api/enter"/>
|
||||
<language userSelectionEnabled="true" />
|
||||
```
|
||||
|
||||
You will see the word `HOST` where there would be configured hostname/IP address. You need to change the text `HOST` to the IP address (or hostname) of your BigBlueButton server. For example, if the IP address of your BigBlueButton server is `192.168.1.145`, then using the following command you can easily substitute all occurrences of `HOST` with `192.168.1.145`.
|
||||
|
||||
Note: Don't copy-and-paste the following command as-is: the address `192.168.1.145` is likely not the correct IP address (or hostname) for your BigBlueButton server. Substitute the IP address (or hostname) for your BigBlueButton server.
|
||||
|
||||
```
|
||||
sed -i s/HOST/192.168.1.145/g bigbluebutton-client/src/conf/config.xml
|
||||
```
|
||||
|
||||
After you've done the above command, take a quick look at the file and ensure all instances of `HOST` are properly replaced with the IP address (or hostname) of your BigBlueButton server.
|
||||
|
||||
The `config.xml` is ultimately loaded by the BigBlueButton client when a user joins a session on the server.
|
||||
|
||||
Later on, when you deploy your modified client to the BigBlueButton server, there will be two BigBlueButton clients on your server: your modified BigBlueButton client and the default BigBlueButton packaged client (again, this is good as you can switch back and forth). However, the BigBlueButton configuration command `sudo bbb-conf ` only modifies the packaged BigBlueButton client and you will need to mirror any changes to the packaged config.xml to the secondary client's config.xml.
|
||||
|
||||
Next, you need to setup nginx to redirect calls to the client towards your development version. If you don't already have nginx client development file at `/etc/bigbluebutton/nginx/client_dev`, create one with the following command.
|
||||
|
||||
**NOTE:** Make sure to replace "firstuser" with your own username if it's different.
|
||||
|
||||
```
|
||||
echo "
|
||||
location /client/BigBlueButton.html {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
expires 1m;
|
||||
}
|
||||
|
||||
# BigBlueButton Flash client.
|
||||
location /client {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
}
|
||||
" | sudo tee /etc/bigbluebutton/nginx/client_dev
|
||||
```
|
||||
|
||||
Check the contents to ensure it matches below.
|
||||
|
||||
Again, make sure you change `/home/firstuser` to match your home directory.
|
||||
|
||||
```
|
||||
$ cat /etc/bigbluebutton/nginx/client_dev
|
||||
|
||||
location /client/BigBlueButton.html {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
expires 1m;
|
||||
}
|
||||
|
||||
# BigBlueButton Flash client.
|
||||
location /client {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
}
|
||||
```
|
||||
|
||||
These rules tell nginx where to find the BigBlueButton client. Currently, nginx is using the rules with the default BigBlueButton client through a symbolic link.
|
||||
|
||||
```
|
||||
$ ls -al /etc/bigbluebutton/nginx/client.nginx
|
||||
lrwxrwxrwx 1 root root 31 2013-05-05 15:44 /etc/bigbluebutton/nginx/client.nginx -> /etc/bigbluebutton/nginx/client
|
||||
```
|
||||
|
||||
Modify this symbolic link so it points to the development directory for your BigBlueButton client.
|
||||
|
||||
```
|
||||
sudo ln -f -s /etc/bigbluebutton/nginx/client_dev /etc/bigbluebutton/nginx/client.nginx
|
||||
```
|
||||
|
||||
Check that the modifications are in place.
|
||||
|
||||
```
|
||||
$ ls -al /etc/bigbluebutton/nginx/client.nginx
|
||||
lrwxrwxrwx 1 root root 35 2013-05-05 21:07 /etc/bigbluebutton/nginx/client.nginx -> /etc/bigbluebutton/nginx/client_dev
|
||||
```
|
||||
|
||||
Now we need to restart nginx so our changes take effect.
|
||||
|
||||
```
|
||||
$ sudo service nginx restart
|
||||
Restarting nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
configuration file /etc/nginx/nginx.conf test is successful nginx.
|
||||
```
|
||||
|
||||
Now, when you launch the BigBlueButton client, nginx will serve the client from your development directory. Next, we need to rebuild the client.
|
||||
|
||||
## Building the client
|
||||
Let's now build the client. Note we're going to build and run the client to make sure it works before making any changes to the source.
|
||||
|
||||
First, we'll build the locales (language translation files). If you are not modifying the locales, you only need to do this once.
|
||||
On you bbb-client terminal, run the following commands.
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bigbluebutton-client
|
||||
@ -392,42 +48,12 @@ ant
|
||||
This will create a build of the BigBlueButton client in the `/home/firstuser/dev/bigbluebutton/bigbluebutton-client/client` directory.
|
||||
|
||||
|
||||
## Setup nginx
|
||||
|
||||
Create file `/etc/bigbluebutton/nginx/screenshare.nginx` and add the following:
|
||||
|
||||
```
|
||||
# Handle desktop sharing. Forwards
|
||||
# requests to Red5 on port 5080.
|
||||
location /screenshare {
|
||||
proxy_pass http://127.0.0.1:5080;
|
||||
proxy_redirect default;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
client_max_body_size 10m;
|
||||
client_body_buffer_size 128k;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 90;
|
||||
proxy_read_timeout 90;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 4 32k;
|
||||
proxy_busy_buffers_size 64k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
include fastcgi_params;
|
||||
}
|
||||
```
|
||||
|
||||
Restart nginx
|
||||
|
||||
```
|
||||
sudo service nginx restart
|
||||
```
|
||||
|
||||
## Build BBB Red5 Applications
|
||||
|
||||
Turn off red5 service
|
||||
On your red5 terminal, turn off red5 service
|
||||
|
||||
```
|
||||
sudo service bbb-red5 stop
|
||||
sudo systemctl stop red5
|
||||
```
|
||||
|
||||
You need to make `red5/webapps` writeable. Otherwise, you will get a permission error when you try to deploy into Red5.
|
||||
@ -438,77 +64,48 @@ sudo chmod -R 777 /usr/share/red5/webapps
|
||||
|
||||
### Build common-message
|
||||
|
||||
On your message terminal, run the following commands. Other components depends on this, so build this first.
|
||||
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-common-message/
|
||||
sbt clean
|
||||
|
||||
sbt publish
|
||||
sbt publishLocal
|
||||
```
|
||||
|
||||
### Build bbb-apps
|
||||
|
||||
We've split bbb-apps into bbb-apps-common and bigbluebutton-apps. We need to build bbb-apps-common first.
|
||||
|
||||
On your apps-common terminal, build the bbb-apps-common component.
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-apps-common/
|
||||
|
||||
# Force updating of bbb-commons-message
|
||||
sbt clean
|
||||
|
||||
# Build and share library
|
||||
sbt publish publishLocal
|
||||
```
|
||||
|
||||
On your bbb-apps terminal, run the following commands.
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bigbluebutton-apps/
|
||||
|
||||
# To make sure the lib folder is clean of old dependencies especially if you've used this
|
||||
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
|
||||
# do once.
|
||||
rm lib/*
|
||||
|
||||
# Force updating dependencies (bbb-apps-common)
|
||||
gradle clean
|
||||
|
||||
gradle resolveDeps
|
||||
gradle clean war deploy
|
||||
```
|
||||
|
||||
### Build bbb-voice
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-voice
|
||||
```
|
||||
|
||||
Edit `src/main/webapp/WEB-INF/bigbluebutton-sip.properties`
|
||||
Make sure the IP addresses point to yout BBB server.
|
||||
|
||||
```
|
||||
bbb.sip.app.ip=192.168.74.128
|
||||
|
||||
# The ip and port of the FreeSWITCH server
|
||||
freeswitch.ip=192.168.74.128
|
||||
```
|
||||
|
||||
```
|
||||
gradle resolveDeps
|
||||
gradle clean war deploy
|
||||
```
|
||||
|
||||
### Build bbb-video
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-video/
|
||||
gradle resolveDeps
|
||||
gradle clean war deploy
|
||||
```
|
||||
|
||||
### Build bbb-screenshare
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-screenshare/app
|
||||
```
|
||||
|
||||
Edit `src/main/webapp/WEB-INF/screenshare.properties`
|
||||
Make sure the following points to your BBB server.
|
||||
|
||||
```
|
||||
streamBaseUrl=rtmp://192.168.74.128/screenshare
|
||||
jnlpUrl=http://192.168.74.128/screenshare
|
||||
jnlpFile=http://192.168.74.128/screenshare/screenshare.jnlp
|
||||
```
|
||||
|
||||
Build and deploy
|
||||
|
||||
```
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
## Stop services
|
||||
|
||||
|
||||
```
|
||||
sudo /etc/init.d/bbb-red5 stop
|
||||
sudo service bbb-apps-akka stop
|
||||
sudo service bbb-fsesl-akka stop
|
||||
gradle war deploy
|
||||
```
|
||||
|
||||
Remove old `bbb-web` app from tomcat
|
||||
@ -521,7 +118,7 @@ sudo rm /var/lib/tomcat7/webapps/bigbluebutton.war
|
||||
|
||||
### Run Red5
|
||||
|
||||
Open up a terminal.
|
||||
On your red5 terminal, start red5.
|
||||
|
||||
```
|
||||
cd /usr/share/red5
|
||||
@ -530,24 +127,62 @@ sudo -u red5 ./red5.sh
|
||||
|
||||
### Run Akka Apps
|
||||
|
||||
Open up another terminal.
|
||||
On your akka-apps terminal, start akka-apps
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/akka-bbb-apps
|
||||
|
||||
# To make sure the lib folder is clean of old dependencies especially if you've used this
|
||||
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
|
||||
# do once.
|
||||
rm lib_managed/*
|
||||
|
||||
sbt clean
|
||||
sbt run
|
||||
```
|
||||
|
||||
### Run Akka FSESL App
|
||||
|
||||
Open another terminal
|
||||
On your akka-fsesl terminal, start akka-fsesl
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/akka-bbb-fsesl
|
||||
|
||||
# To make sure the lib folder is clean of old dependencies especially if you've used this
|
||||
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
|
||||
# do once.
|
||||
rm lib_managed/*
|
||||
|
||||
sbt clean
|
||||
sbt run
|
||||
```
|
||||
|
||||
### Build bbb-web
|
||||
|
||||
We've split up bbb-web into bbb-common-web and bigbluebutton-web. We need to build
|
||||
bbb-common-web first.
|
||||
|
||||
On your common-web terminal, run these commands
|
||||
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-common-web/
|
||||
|
||||
# To make sure the lib folder is clean of old dependencies especially if you've used this
|
||||
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
|
||||
# do once.
|
||||
rm lib_managed/*
|
||||
|
||||
# Force updating of dependencies especially bbb-commons-message
|
||||
sbt clean
|
||||
sbt publish publishLocal
|
||||
```
|
||||
|
||||
|
||||
### Run bbb-web
|
||||
|
||||
On your bbb-web terminal, start bbb-web
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bigbluebutton-web
|
||||
```
|
||||
@ -565,8 +200,16 @@ securitySalt=856d5e0197b1aa0cf79897841142a5f6
|
||||
Start bbb-web
|
||||
|
||||
```
|
||||
|
||||
# To make sure the lib folder is clean of old dependencies especially if you've used this
|
||||
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
|
||||
# do once.
|
||||
rm lib/*
|
||||
|
||||
gradle clean
|
||||
gradle resolveDeps
|
||||
grails clean
|
||||
sudo chmod -R ugo+rwx /var/log/bigbluebutton
|
||||
grails -Dserver.port=8888 run-war
|
||||
```
|
||||
|
||||
|
@ -6,7 +6,7 @@ organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.2"
|
||||
|
||||
scalaVersion := "2.11.7"
|
||||
scalaVersion := "2.12.2"
|
||||
|
||||
scalacOptions ++= Seq(
|
||||
"-unchecked",
|
||||
@ -14,7 +14,7 @@ scalacOptions ++= Seq(
|
||||
"-Xlint",
|
||||
"-Ywarn-dead-code",
|
||||
"-language:_",
|
||||
"-target:jvm-1.7",
|
||||
"-target:jvm-1.8",
|
||||
"-encoding", "UTF-8"
|
||||
)
|
||||
|
||||
@ -37,35 +37,59 @@ testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console",
|
||||
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
|
||||
|
||||
val akkaVersion = "2.5.1"
|
||||
val scalaTestV = "2.2.6"
|
||||
|
||||
libraryDependencies ++= {
|
||||
val akkaVersion = "2.3.14"
|
||||
val akkaStreamV = "1.0"
|
||||
val scalaTestV = "2.2.4"
|
||||
Seq(
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-stream-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-core-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-testkit-experimental" % akkaStreamV,
|
||||
"org.scalatest" % "scalatest_2.11" % scalaTestV % "test",
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"commons-codec" % "commons-codec" % "1.10",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "2.5",
|
||||
"redis.clients" % "jedis" % "2.7.2",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.19-SNAPSHOT",
|
||||
"io.spray" %% "spray-json" % "1.3.2"
|
||||
"org.apache.commons" % "commons-lang3" % "3.2"
|
||||
)
|
||||
}
|
||||
|
||||
libraryDependencies += "org.bigbluebutton" % "bbb-common-message_2.12" % "0.0.19-SNAPSHOT"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.scala-lang/scala-library
|
||||
libraryDependencies += "org.scala-lang" % "scala-library" % "2.12.2"
|
||||
// https://mvnrepository.com/artifact/org.scala-lang/scala-compiler
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.12.2"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor_2.12
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-actor_2.12" % "2.5.1"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-slf4j_2.12
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-slf4j_2.12" % "2.5.1"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.github.etaty/rediscala_2.12
|
||||
libraryDependencies += "com.github.etaty" % "rediscala_2.12" % "1.8.0"
|
||||
|
||||
libraryDependencies += "com.softwaremill.quicklens" %% "quicklens" % "1.4.8"
|
||||
libraryDependencies += "com.google.code.gson" % "gson" % "2.8.0"
|
||||
libraryDependencies += "redis.clients" % "jedis" % "2.9.0"
|
||||
libraryDependencies += "joda-time" % "joda-time" % "2.9.9"
|
||||
libraryDependencies += "io.spray" % "spray-json_2.12" % "1.3.3"
|
||||
libraryDependencies += "org.parboiled" % "parboiled-scala_2.12" % "1.1.8"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-scala_2.12
|
||||
libraryDependencies += "com.fasterxml.jackson.module" % "jackson-module-scala_2.12" % "2.8.8"
|
||||
|
||||
|
||||
|
||||
// For generating test reports
|
||||
libraryDependencies += "org.pegdown" % "pegdown" % "1.6.0" % "test"
|
||||
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-testkit_2.12
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-testkit_2.12" % "2.5.1" % "test"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.scalactic/scalactic_2.12
|
||||
libraryDependencies += "org.scalactic" % "scalactic_2.12" % "3.0.3" % "test"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.scalatest/scalatest_2.12
|
||||
libraryDependencies += "org.scalatest" % "scalatest_2.12" % "3.0.3" % "test"
|
||||
|
||||
libraryDependencies += "org.mockito" % "mockito-core" % "2.7.22" % "test"
|
||||
|
||||
|
||||
seq(Revolver.settings: _*)
|
||||
|
||||
|
@ -103,7 +103,8 @@ public interface IBigBlueButtonInGW {
|
||||
String layout);
|
||||
|
||||
// Chat
|
||||
void getChatHistory(String meetingID, String requesterID, String replyTo);
|
||||
void getAllChatHistory(String meetingID, String requesterID, String replyTo);
|
||||
void getChatHistory(String meetingID, String requesterID, String replyTo, String chatId);
|
||||
void sendPublicMessage(String meetingID, String requesterID, Map<String, String> message);
|
||||
void sendPrivateMessage(String meetingID, String requesterID, Map<String, String> message);
|
||||
void clearPublicChatHistory(String meetingID, String requesterID);
|
||||
|
@ -30,7 +30,8 @@ public class ChatMessageReceiver implements MessageHandler{
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (GetChatHistoryRequestMessage.GET_CHAT_HISTORY_REQUEST.equals(messageName)) {
|
||||
GetChatHistoryRequestMessage msg = GetChatHistoryRequestMessage.fromJson(message);
|
||||
bbbGW.getChatHistory(msg.meetingId, msg.requesterId, msg.replyTo);
|
||||
// TODO: Add chatId to getChatHistory message
|
||||
bbbGW.getChatHistory(msg.meetingId, msg.requesterId, msg.replyTo, "FIXME!");
|
||||
} else if (SendPublicChatMessage.SEND_PUBLIC_CHAT_MESSAGE.equals(messageName)){
|
||||
SendPublicChatMessage msg = SendPublicChatMessage.fromJson(message);
|
||||
bbbGW.sendPublicMessage(msg.meetingId, msg.requesterId, msg.messageInfo);
|
||||
|
@ -62,3 +62,10 @@ red5 {
|
||||
deskshareip="192.168.0.109"
|
||||
deskshareapp="video-broadcast"
|
||||
}
|
||||
|
||||
eventBus {
|
||||
meetingManagerChannel = "MeetingManagerChannel"
|
||||
outMessageChannel = "OutgoingMessageChannel"
|
||||
incomingJsonMsgChannel = "IncomingJsonMsgChannel"
|
||||
outBbbMsgMsgChannel = "OutBbbMsgChannel"
|
||||
}
|
||||
|
@ -1,27 +1,22 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import akka.event.{ LoggingAdapter, Logging }
|
||||
import akka.actor.{ ActorSystem, Props }
|
||||
import scala.concurrent.duration._
|
||||
import redis.RedisClient
|
||||
import scala.concurrent.{ Future, Await }
|
||||
import akka.event.Logging
|
||||
import akka.actor.ActorSystem
|
||||
import org.bigbluebutton.endpoint.redis.RedisPublisher
|
||||
import org.bigbluebutton.endpoint.redis.KeepAliveRedisPublisher
|
||||
import org.bigbluebutton.endpoint.redis.AppsRedisSubscriberActor
|
||||
import org.bigbluebutton.core.api.MessageOutGateway
|
||||
import org.bigbluebutton.core.api.IBigBlueButtonInGW
|
||||
import org.bigbluebutton.core.BigBlueButtonInGW
|
||||
import org.bigbluebutton.core.MessageSender
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.MessageSenderActor
|
||||
import org.bigbluebutton.core.RecorderActor
|
||||
import org.bigbluebutton.core.pubsub.receivers.RedisMessageReceiver
|
||||
import org.bigbluebutton.core.api.OutMessageListener2
|
||||
import org.bigbluebutton.core.pubsub.senders._
|
||||
import org.bigbluebutton.core.service.recorder.RedisDispatcher
|
||||
import org.bigbluebutton.core.service.recorder.RecorderApplication
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.JsonMessageSenderActor
|
||||
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
|
||||
import org.bigbluebutton.core.recorder.RecorderActor
|
||||
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
|
||||
|
||||
object Boot extends App with SystemConfiguration {
|
||||
|
||||
@ -31,8 +26,8 @@ object Boot extends App with SystemConfiguration {
|
||||
|
||||
val eventBus = new IncomingEventBus
|
||||
val outgoingEventBus = new OutgoingEventBus
|
||||
|
||||
val outGW = new OutMessageGateway(outgoingEventBus)
|
||||
val outBus2 = new OutEventBus2
|
||||
val outGW = new OutMessageGateway(outgoingEventBus, outBus2)
|
||||
|
||||
val redisPublisher = new RedisPublisher(system)
|
||||
val msgSender = new MessageSender(redisPublisher)
|
||||
@ -45,14 +40,23 @@ object Boot extends App with SystemConfiguration {
|
||||
val recorderActor = system.actorOf(RecorderActor.props(recorderApp), "recorderActor")
|
||||
val newMessageSenderActor = system.actorOf(JsonMessageSenderActor.props(msgSender), "newMessageSenderActor")
|
||||
|
||||
outgoingEventBus.subscribe(messageSenderActor, "outgoingMessageChannel")
|
||||
outgoingEventBus.subscribe(recorderActor, "outgoingMessageChannel")
|
||||
outgoingEventBus.subscribe(newMessageSenderActor, "outgoingMessageChannel")
|
||||
outgoingEventBus.subscribe(messageSenderActor, outMessageChannel)
|
||||
outgoingEventBus.subscribe(recorderActor, outMessageChannel)
|
||||
outgoingEventBus.subscribe(newMessageSenderActor, outMessageChannel)
|
||||
val incomingJsonMessageBus = new IncomingJsonMessageBus
|
||||
|
||||
val bbbInGW = new BigBlueButtonInGW(system, eventBus, outGW, red5DeskShareIP, red5DeskShareApp)
|
||||
val bbbMsgBus = new BbbMsgRouterEventBus
|
||||
|
||||
val fromAkkaAppsMsgSenderActorRef = system.actorOf(FromAkkaAppsMsgSenderActor.props(msgSender))
|
||||
outBus2.subscribe(fromAkkaAppsMsgSenderActorRef, outBbbMsgMsgChannel)
|
||||
|
||||
val bbbInGW = new BigBlueButtonInGW(system, eventBus, bbbMsgBus, outGW)
|
||||
val redisMsgReceiver = new RedisMessageReceiver(bbbInGW)
|
||||
|
||||
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(redisMsgReceiver), "redis-subscriber")
|
||||
val redisMessageHandlerActor = system.actorOf(ReceivedJsonMsgHandlerActor.props(bbbMsgBus, incomingJsonMessageBus))
|
||||
incomingJsonMessageBus.subscribe(redisMessageHandlerActor, toAkkaAppsJsonChannel)
|
||||
|
||||
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(redisMsgReceiver, incomingJsonMessageBus), "redis-subscriber")
|
||||
|
||||
val keepAliveRedisPublisher = new KeepAliveRedisPublisher(system, redisPublisher)
|
||||
}
|
||||
|
@ -27,4 +27,19 @@ trait SystemConfiguration {
|
||||
|
||||
lazy val expireLastUserLeft = Try(config.getInt("expire.lastUserLeft")).getOrElse(60) // 1 minute
|
||||
lazy val expireNeverJoined = Try(config.getInt("expire.neverJoined")).getOrElse(5 * 60) // 5 minutes
|
||||
|
||||
lazy val meetingManagerChannel = Try(config.getString("eventBus.meetingManagerChannel")).getOrElse("MeetingManagerChannel")
|
||||
lazy val outMessageChannel = Try(config.getString("eventBus.outMessageChannel")).getOrElse("OutgoingMessageChannel")
|
||||
lazy val incomingJsonMsgChannel = Try(config.getString("eventBus.incomingJsonMsgChannel")).getOrElse("IncomingJsonMsgChannel")
|
||||
lazy val outBbbMsgMsgChannel = Try(config.getString("eventBus.outBbbMsgMsgChannel")).getOrElse("OutBbbMsgChannel")
|
||||
|
||||
lazy val toAkkaAppsRedisChannel = Try(config.getString("redis.toAkkaAppsRedisChannel")).getOrElse("to-akka-apps-redis-channel")
|
||||
lazy val fromAkkaAppsRedisChannel = Try(config.getString("redis.fromAkkaAppsRedisChannel")).getOrElse("from-akka-apps-redis-channel")
|
||||
lazy val fromAkkaAppsChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-channel")
|
||||
lazy val toAkkaAppsChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-channel")
|
||||
lazy val fromClientChannel = Try(config.getString("eventBus.fromClientChannel")).getOrElse("from-client-channel")
|
||||
lazy val toClientChannel = Try(config.getString("eventBus.toClientChannel")).getOrElse("to-client-channel")
|
||||
lazy val toAkkaAppsJsonChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-json-channel")
|
||||
lazy val fromAkkaAppsJsonChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-json-channel")
|
||||
lazy val fromAkkaAppsOldJsonChannel = Try(config.getString("eventBus.fromAkkaAppsOldChannel")).getOrElse("from-akka-apps-old-json-channel")
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import akka.pattern.{ ask, pipe }
|
||||
import akka.util.Timeout
|
||||
|
||||
import scala.concurrent.duration._
|
||||
@ -13,22 +13,29 @@ import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.bigbluebutton.common2.messages.MessageBody.MeetingCreatedEvtBody
|
||||
import org.bigbluebutton.common2.messages._
|
||||
import org.bigbluebutton.core.running.RunningMeeting
|
||||
import org.bigbluebutton.core2.RunningMeetings
|
||||
import org.bigbluebutton.core2.message.handlers.CreateMeetingReqMsgHdlr
|
||||
|
||||
object BigBlueButtonActor extends SystemConfiguration {
|
||||
def props(system: ActorSystem,
|
||||
eventBus: IncomingEventBus,
|
||||
bbbMsgBus: BbbMsgRouterEventBus,
|
||||
outGW: OutMessageGateway): Props =
|
||||
Props(classOf[BigBlueButtonActor], system, eventBus, outGW)
|
||||
Props(classOf[BigBlueButtonActor], system, eventBus, bbbMsgBus, outGW)
|
||||
}
|
||||
|
||||
class BigBlueButtonActor(val system: ActorSystem,
|
||||
eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging {
|
||||
val eventBus: IncomingEventBus, val bbbMsgBus: BbbMsgRouterEventBus,
|
||||
val outGW: OutMessageGateway) extends Actor
|
||||
with ActorLogging with SystemConfiguration {
|
||||
|
||||
implicit def executionContext = system.dispatcher
|
||||
implicit val timeout = Timeout(5 seconds)
|
||||
|
||||
private var meetings = new collection.immutable.HashMap[String, RunningMeeting]
|
||||
private val meetings = new RunningMeetings
|
||||
|
||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||
case e: Exception => {
|
||||
@ -40,8 +47,19 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
}
|
||||
}
|
||||
|
||||
override def preStart() {
|
||||
bbbMsgBus.subscribe(self, meetingManagerChannel)
|
||||
}
|
||||
|
||||
override def postStop() {
|
||||
bbbMsgBus.unsubscribe(self, meetingManagerChannel)
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case msg: CreateMeeting => handleCreateMeeting(msg)
|
||||
// 2x messages
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
|
||||
// 1x messages
|
||||
case msg: DestroyMeeting => handleDestroyMeeting(msg)
|
||||
case msg: KeepAliveMessage => handleKeepAliveMessage(msg)
|
||||
case msg: PubSubPing => handlePubSubPingMessage(msg)
|
||||
@ -56,8 +74,71 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
msg.core match {
|
||||
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
|
||||
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
||||
case _ => println("***** Cannot handle " + msg.envelope.name)
|
||||
}
|
||||
}
|
||||
|
||||
def handleRegisterUserReqMsg(msg: RegisterUserReqMsg): Unit = {
|
||||
log.debug("****** RECEIVED RegisterUserReqMsg msg {}", msg)
|
||||
for {
|
||||
m <- RunningMeetings.findWithId(meetings, msg.header.meetingId)
|
||||
} yield {
|
||||
m.actorRef forward (msg)
|
||||
}
|
||||
}
|
||||
|
||||
def handleCreateMeetingReqMsg(msg: CreateMeetingReqMsg): Unit = {
|
||||
log.debug("****** RECEIVED CreateMeetingReqMsg msg {}", msg)
|
||||
|
||||
RunningMeetings.findWithId(meetings, msg.body.props.meetingProp.intId) match {
|
||||
case None => {
|
||||
log.info("Create meeting request. meetingId={}", msg.body.props.meetingProp.intId)
|
||||
|
||||
val m = RunningMeeting(msg.body.props, outGW, eventBus)
|
||||
|
||||
/** Subscribe to meeting and voice events. **/
|
||||
eventBus.subscribe(m.actorRef, m.props.meetingProp.intId)
|
||||
eventBus.subscribe(m.actorRef, m.props.voiceProp.voiceConf)
|
||||
eventBus.subscribe(m.actorRef, m.props.screenshareProps.screenshareConf)
|
||||
|
||||
bbbMsgBus.subscribe(m.actorRef, m.props.meetingProp.intId)
|
||||
|
||||
RunningMeetings.add(meetings, m)
|
||||
|
||||
// Send old message format
|
||||
outGW.send(new MeetingCreated(m.props.meetingProp.intId,
|
||||
m.props.meetingProp.extId, m.props.breakoutProps.parentId,
|
||||
m.props.recordProp.record, m.props.meetingProp.name,
|
||||
m.props.voiceProp.voiceConf, m.props.durationProps.duration,
|
||||
m.props.password.moderatorPass, m.props.password.viewerPass,
|
||||
m.props.durationProps.createdTime, m.props.durationProps.createdDate,
|
||||
m.props.meetingProp.isBreakout))
|
||||
|
||||
m.actorRef ! new InitializeMeeting(m.props.meetingProp.intId, m.props.recordProp.record)
|
||||
|
||||
// Send new 2x message
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(MeetingCreatedEvtMsg.NAME, routing)
|
||||
val header = BbbCoreBaseHeader(MeetingCreatedEvtMsg.NAME)
|
||||
val body = MeetingCreatedEvtBody(msg.body.props)
|
||||
val event = MeetingCreatedEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
case Some(m) => {
|
||||
log.info("Meeting already created. meetingID={}", msg.body.props.meetingProp.intId)
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def findMeetingWithVoiceConfId(voiceConfId: String): Option[RunningMeeting] = {
|
||||
meetings.values.find(m => { m.mProps.voiceBridge == voiceConfId })
|
||||
RunningMeetings.findMeetingWithVoiceConfId(meetings, voiceConfId)
|
||||
}
|
||||
|
||||
private def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) {
|
||||
@ -96,25 +177,31 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
}
|
||||
|
||||
private def handleValidateAuthToken(msg: ValidateAuthToken) {
|
||||
meetings.get(msg.meetingID) foreach { m =>
|
||||
m.actorRef ! msg
|
||||
|
||||
// val future = m.actorRef.ask(msg)(5 seconds)
|
||||
// future onComplete {
|
||||
// case Success(result) => {
|
||||
// log.info("Validate auth token response. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
// /**
|
||||
// * Received a reply from MeetingActor which means hasn't hung!
|
||||
// * Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
|
||||
// * audit to check whether the actor is still alive. (ralam feb 25, 2015)
|
||||
// */
|
||||
// }
|
||||
// case Failure(failure) => {
|
||||
// log.warning("Validate auth token timeout. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
// outGW.send(new ValidateAuthTokenTimedOut(msg.meetingID, msg.userId, msg.token, false, msg.correlationId))
|
||||
// }
|
||||
// }
|
||||
for {
|
||||
m <- RunningMeetings.findWithId(meetings, msg.meetingID)
|
||||
} yield {
|
||||
m.actorRef forward (msg)
|
||||
}
|
||||
|
||||
//meetings.get(msg.meetingID) foreach { m =>
|
||||
// m.actorRef ! msg
|
||||
|
||||
// val future = m.actorRef.ask(msg)(5 seconds)
|
||||
// future onComplete {
|
||||
// case Success(result) => {
|
||||
// log.info("Validate auth token response. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
// /**
|
||||
// * Received a reply from MeetingActor which means hasn't hung!
|
||||
// * Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
|
||||
// * audit to check whether the actor is still alive. (ralam feb 25, 2015)
|
||||
// */
|
||||
// }
|
||||
// case Failure(failure) => {
|
||||
// log.warning("Validate auth token timeout. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
// outGW.send(new ValidateAuthTokenTimedOut(msg.meetingID, msg.userId, msg.token, false, msg.correlationId))
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private def handleKeepAliveMessage(msg: KeepAliveMessage): Unit = {
|
||||
@ -127,6 +214,41 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
|
||||
private def handleDestroyMeeting(msg: DestroyMeeting) {
|
||||
log.info("Received DestroyMeeting message for meetingId={}", msg.meetingID)
|
||||
|
||||
for {
|
||||
m <- RunningMeetings.findWithId(meetings, msg.meetingID)
|
||||
m2 <- RunningMeetings.remove(meetings, msg.meetingID)
|
||||
} yield {
|
||||
log.info("Kick everyone out on meetingId={}", msg.meetingID)
|
||||
if (m.props.meetingProp.isBreakout) {
|
||||
log.info("Informing parent meeting {} that a breakout room has been ended {}",
|
||||
m.props.breakoutProps.parentId, m.props.meetingProp.intId)
|
||||
eventBus.publish(BigBlueButtonEvent(m.props.breakoutProps.parentId,
|
||||
BreakoutRoomEnded(m.props.breakoutProps.parentId, m.props.meetingProp.intId)))
|
||||
}
|
||||
|
||||
// Eject all users using the client.
|
||||
outGW.send(new EndAndKickAll(msg.meetingID, m.props.recordProp.record))
|
||||
// Eject all users from the voice conference
|
||||
outGW.send(new EjectAllVoiceUsers(msg.meetingID, m.props.recordProp.record, m.props.voiceProp.voiceConf))
|
||||
|
||||
// Delay sending DisconnectAllUsers because of RTMPT connection being dropped before UserEject message arrives to the client
|
||||
context.system.scheduler.scheduleOnce(Duration.create(2500, TimeUnit.MILLISECONDS)) {
|
||||
// Disconnect all clients
|
||||
outGW.send(new DisconnectAllUsers(msg.meetingID))
|
||||
log.info("Destroyed meetingId={}", msg.meetingID)
|
||||
outGW.send(new MeetingDestroyed(msg.meetingID))
|
||||
|
||||
/** Unsubscribe to meeting and voice events. **/
|
||||
eventBus.unsubscribe(m.actorRef, m.props.meetingProp.intId)
|
||||
eventBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf)
|
||||
|
||||
// Stop the meeting actor.
|
||||
context.stop(m.actorRef)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
meetings.get(msg.meetingID) match {
|
||||
case None => log.info("Could not find meetingId={}", msg.meetingID)
|
||||
case Some(m) => {
|
||||
@ -150,7 +272,7 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
log.info("Destroyed meetingId={}", msg.meetingID)
|
||||
outGW.send(new MeetingDestroyed(msg.meetingID))
|
||||
|
||||
/** Unsubscribe to meeting and voice events. **/
|
||||
// Unsubscribe to meeting and voice events.
|
||||
eventBus.unsubscribe(m.actorRef, m.mProps.meetingID)
|
||||
eventBus.unsubscribe(m.actorRef, m.mProps.voiceBridge)
|
||||
|
||||
@ -159,45 +281,20 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleCreateMeeting(msg: CreateMeeting): Unit = {
|
||||
meetings.get(msg.meetingID) match {
|
||||
case None => {
|
||||
log.info("Create meeting request. meetingId={}", msg.mProps.meetingID)
|
||||
|
||||
var m = RunningMeeting(msg.mProps, outGW, eventBus)
|
||||
|
||||
/** Subscribe to meeting and voice events. **/
|
||||
eventBus.subscribe(m.actorRef, m.mProps.meetingID)
|
||||
eventBus.subscribe(m.actorRef, m.mProps.voiceBridge)
|
||||
eventBus.subscribe(m.actorRef, m.mProps.deskshareBridge)
|
||||
|
||||
meetings += m.mProps.meetingID -> m
|
||||
outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.parentMeetingID,
|
||||
m.mProps.recorded, m.mProps.meetingName, m.mProps.voiceBridge, msg.mProps.duration, msg.mProps.moderatorPass,
|
||||
msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate, msg.mProps.isBreakout))
|
||||
|
||||
m.actorRef ! new InitializeMeeting(m.mProps.meetingID, m.mProps.recorded)
|
||||
}
|
||||
case Some(m) => {
|
||||
log.info("Meeting already created. meetingID={}", msg.mProps.meetingID)
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private def handleGetAllMeetingsRequest(msg: GetAllMeetingsRequest) {
|
||||
val len = meetings.keys.size
|
||||
val len = RunningMeetings.numMeetings(meetings)
|
||||
var currPosition = len - 1
|
||||
var resultArray: Array[MeetingInfo] = new Array[MeetingInfo](len)
|
||||
val resultArray: Array[MeetingInfo] = new Array[MeetingInfo](len)
|
||||
|
||||
meetings.values.foreach(m => {
|
||||
val id = m.mProps.meetingID
|
||||
val duration = m.mProps.duration
|
||||
val name = m.mProps.meetingName
|
||||
val recorded = m.mProps.recorded
|
||||
val voiceBridge = m.mProps.voiceBridge
|
||||
RunningMeetings.meetings(meetings).foreach(m => {
|
||||
val id = m.props.meetingProp.intId
|
||||
val duration = m.props.durationProps.duration
|
||||
val name = m.props.meetingProp.name
|
||||
val recorded = m.props.recordProp.record
|
||||
val voiceBridge = m.props.voiceProp.voiceConf
|
||||
|
||||
val info = new MeetingInfo(id, name, recorded, voiceBridge, duration)
|
||||
resultArray(currPosition) = info
|
||||
@ -212,7 +309,7 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetPresentationInfo(id, html5clientRequesterID, html5clientRequesterID)))
|
||||
|
||||
//send chat history
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetChatHistoryRequest(id, html5clientRequesterID, html5clientRequesterID)))
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetAllChatHistoryRequest(id, html5clientRequesterID, html5clientRequesterID)))
|
||||
|
||||
//send lock settings
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetLockSettings(id, html5clientRequesterID)))
|
||||
|
@ -2,6 +2,7 @@ package org.bigbluebutton.core
|
||||
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.api._
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
import org.bigbluebutton.core.apps.Page
|
||||
import org.bigbluebutton.core.apps.Presentation
|
||||
@ -12,17 +13,20 @@ import org.bigbluebutton.common.messages.StartCustomPollRequestMessage
|
||||
import org.bigbluebutton.common.messages.PubSubPingMessage
|
||||
import org.bigbluebutton.messages._
|
||||
import akka.event.Logging
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.core.models.Roles
|
||||
|
||||
class BigBlueButtonInGW(
|
||||
val system: ActorSystem,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway,
|
||||
val red5DeskShareIP: String,
|
||||
val red5DeskShareApp: String) extends IBigBlueButtonInGW {
|
||||
bbbMsgBus: BbbMsgRouterEventBus,
|
||||
outGW: OutMessageGateway) extends IBigBlueButtonInGW with SystemConfiguration {
|
||||
|
||||
val log = Logging(system, getClass)
|
||||
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, outGW), "bigbluebutton-actor")
|
||||
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW), "bigbluebutton-actor")
|
||||
eventBus.subscribe(bbbActor, meetingManagerChannel)
|
||||
|
||||
/** For OLD Messaged **/
|
||||
eventBus.subscribe(bbbActor, "meeting-manager")
|
||||
|
||||
def handleBigBlueButtonMessage(message: IBigBlueButtonMessage) {
|
||||
@ -46,7 +50,7 @@ class BigBlueButtonInGW(
|
||||
//default
|
||||
case undef => GuestPolicy.ASK_MODERATOR
|
||||
}
|
||||
|
||||
/*
|
||||
val mProps = new MeetingProperties(
|
||||
msg.payload.id,
|
||||
msg.payload.externalId,
|
||||
@ -71,6 +75,7 @@ class BigBlueButtonInGW(
|
||||
)
|
||||
|
||||
eventBus.publish(BigBlueButtonEvent("meeting-manager", new CreateMeeting(msg.payload.id, mProps)))
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,8 +162,8 @@ class BigBlueButtonInGW(
|
||||
val disablePrivChat = s.getOrElse("disablePrivateChat", false)
|
||||
val disablePubChat = s.getOrElse("disablePublicChat", false)
|
||||
val lockedLayout = s.getOrElse("lockedLayout", false)
|
||||
var lockOnJoin = s.getOrElse("lockOnJoin", false)
|
||||
var lockOnJoinConfigurable = s.getOrElse("lockOnJoinConfigurable", false)
|
||||
val lockOnJoin = s.getOrElse("lockOnJoin", false)
|
||||
val lockOnJoinConfigurable = s.getOrElse("lockOnJoinConfigurable", false)
|
||||
|
||||
val permissions = new Permissions(disableCam = disableCam,
|
||||
disableMic = disableMic,
|
||||
@ -331,22 +336,21 @@ class BigBlueButtonInGW(
|
||||
|
||||
def generatePresentationPages(presId: String, numPages: Int, presBaseUrl: String): scala.collection.immutable.HashMap[String, Page] = {
|
||||
var pages = new scala.collection.immutable.HashMap[String, Page]
|
||||
val baseUrl =
|
||||
for (i <- 1 to numPages) {
|
||||
val id = presId + "/" + i
|
||||
val num = i;
|
||||
val current = if (i == 1) true else false
|
||||
val thumbnail = presBaseUrl + "/thumbnail/" + i
|
||||
val swfUri = presBaseUrl + "/slide/" + i
|
||||
for (i <- 1 to numPages) {
|
||||
val id = presId + "/" + i
|
||||
val num = i;
|
||||
val current = if (i == 1) true else false
|
||||
val thumbnail = presBaseUrl + "/thumbnail/" + i
|
||||
val swfUri = presBaseUrl + "/slide/" + i
|
||||
|
||||
val txtUri = presBaseUrl + "/textfiles/" + i
|
||||
val svgUri = presBaseUrl + "/svg/" + i
|
||||
val txtUri = presBaseUrl + "/textfiles/" + i
|
||||
val svgUri = presBaseUrl + "/svg/" + i
|
||||
|
||||
val p = new Page(id = id, num = num, thumbUri = thumbnail, swfUri = swfUri,
|
||||
txtUri = txtUri, svgUri = svgUri,
|
||||
current = current)
|
||||
pages += (p.id -> p)
|
||||
}
|
||||
val p = new Page(id = id, num = num, thumbUri = thumbnail, swfUri = swfUri,
|
||||
txtUri = txtUri, svgUri = svgUri,
|
||||
current = current)
|
||||
pages += (p.id -> p)
|
||||
}
|
||||
|
||||
pages
|
||||
}
|
||||
@ -413,8 +417,12 @@ class BigBlueButtonInGW(
|
||||
* *****************************************************************
|
||||
*/
|
||||
|
||||
def getChatHistory(meetingID: String, requesterID: String, replyTo: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetChatHistoryRequest(meetingID, requesterID, replyTo)))
|
||||
def getAllChatHistory(meetingID: String, requesterID: String, replyTo: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetAllChatHistoryRequest(meetingID, requesterID, replyTo)))
|
||||
}
|
||||
|
||||
def getChatHistory(meetingID: String, requesterID: String, replyTo: String, chatId: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetChatHistoryRequest(meetingID, requesterID, replyTo, chatId)))
|
||||
}
|
||||
|
||||
def sendPublicMessage(meetingID: String, requesterID: String, message: java.util.Map[String, String]) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import spray.json.{ JsObject, JsonParser, DeserializationException }
|
||||
import org.parboiled.errors.ParsingException
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.messages._
|
||||
|
@ -1,29 +1,10 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.Props
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.common.messages.MessagingConstants
|
||||
import org.bigbluebutton.core.pubsub.senders.ChatMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.PesentationMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
|
||||
import org.bigbluebutton.common.messages.PresentationRemovedMessage
|
||||
import org.bigbluebutton.core.apps.Page
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import org.bigbluebutton.core.apps.SimplePollResultOutVO
|
||||
import org.bigbluebutton.core.apps.SimplePollOutVO
|
||||
import org.bigbluebutton.core.pubsub.senders.UsersMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages._
|
||||
import org.bigbluebutton.core.pubsub.senders.WhiteboardMessageToJsonConverter
|
||||
import org.bigbluebutton.common.converters.ToJsonEncoder
|
||||
import org.bigbluebutton.common.messages.payload._
|
||||
import org.bigbluebutton.common.messages._
|
||||
import org.bigbluebutton.messages.payload._
|
||||
import org.bigbluebutton.messages._
|
||||
|
||||
|
@ -7,12 +7,6 @@ import java.util.concurrent.TimeUnit
|
||||
import org.bigbluebutton.core.api.GuestPolicy.GuestPolicy
|
||||
|
||||
case object StopMeetingActor
|
||||
case class MeetingProperties(meetingID: String, externalMeetingID: String, parentMeetingID: String, meetingName: String,
|
||||
recorded: Boolean, voiceBridge: String, deskshareBridge: String, duration: Int,
|
||||
autoStartRecording: Boolean, allowStartStopRecording: Boolean, webcamsOnlyForModerator: Boolean,
|
||||
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String,
|
||||
red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean, sequence: Int,
|
||||
metadata: collection.immutable.Map[String, String], guestPolicy: GuestPolicy)
|
||||
|
||||
case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
|
||||
sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
|
||||
@ -22,9 +16,9 @@ class MeetingModel {
|
||||
private var audioSettingsInited = false
|
||||
private var permissionsInited = false
|
||||
private var permissions = new Permissions()
|
||||
private var recording = false;
|
||||
private var recording = false
|
||||
private var broadcastingRTMP = false
|
||||
private var muted = false;
|
||||
private var muted = false
|
||||
private var meetingEnded = false
|
||||
private var meetingMuted = false
|
||||
private var guestPolicy = GuestPolicy.ASK_MODERATOR
|
||||
@ -40,104 +34,103 @@ class MeetingModel {
|
||||
private var desktopShareVideoHeight = 0
|
||||
|
||||
private var extension = new MeetingExtensionProp
|
||||
/*
|
||||
val startedOn = timeNowInSeconds;
|
||||
|
||||
val startedOn = timeNowInSeconds;
|
||||
var breakoutRoomsStartedOn: Long = 0
|
||||
var breakoutRoomsdurationInMinutes: Int = 0
|
||||
|
||||
var breakoutRoomsStartedOn: Long = 0;
|
||||
var breakoutRoomsdurationInMinutes: Int = 0;
|
||||
|
||||
def resetDesktopSharingParams() = {
|
||||
broadcastingRTMP = false
|
||||
deskShareStarted = false
|
||||
rtmpBroadcastingUrl = ""
|
||||
desktopShareVideoWidth = 0
|
||||
desktopShareVideoHeight = 0
|
||||
}
|
||||
|
||||
def getDeskShareStarted(): Boolean = {
|
||||
return deskShareStarted
|
||||
}
|
||||
|
||||
def setDeskShareStarted(b: Boolean) {
|
||||
deskShareStarted = b
|
||||
println("---deskshare status changed to:" + b)
|
||||
}
|
||||
|
||||
def setDesktopShareVideoWidth(videoWidth: Int) {
|
||||
desktopShareVideoWidth = videoWidth
|
||||
}
|
||||
|
||||
def setDesktopShareVideoHeight(videoHeight: Int) {
|
||||
desktopShareVideoHeight = videoHeight
|
||||
}
|
||||
|
||||
def getDesktopShareVideoWidth(): Int = {
|
||||
desktopShareVideoWidth
|
||||
}
|
||||
|
||||
def getDesktopShareVideoHeight(): Int = {
|
||||
desktopShareVideoHeight
|
||||
}
|
||||
|
||||
def broadcastingRTMPStarted() {
|
||||
broadcastingRTMP = true
|
||||
}
|
||||
|
||||
def isBroadcastingRTMP(): Boolean = {
|
||||
broadcastingRTMP
|
||||
}
|
||||
|
||||
def broadcastingRTMPStopped() {
|
||||
broadcastingRTMP = false
|
||||
}
|
||||
|
||||
def setRTMPBroadcastingUrl(path: String) {
|
||||
println("---RTMP broadcastUrl changed to:" + path)
|
||||
rtmpBroadcastingUrl = path
|
||||
}
|
||||
|
||||
def getRTMPBroadcastingUrl(): String = {
|
||||
rtmpBroadcastingUrl
|
||||
}
|
||||
|
||||
def isExtensionAllowed(): Boolean = extension.numExtensions < extension.maxExtensions
|
||||
def incNumExtension(): Int = {
|
||||
if (extension.numExtensions < extension.maxExtensions) {
|
||||
extension = extension.copy(numExtensions = extension.numExtensions + 1); extension.numExtensions
|
||||
def resetDesktopSharingParams() = {
|
||||
broadcastingRTMP = false
|
||||
deskShareStarted = false
|
||||
rtmpBroadcastingUrl = ""
|
||||
desktopShareVideoWidth = 0
|
||||
desktopShareVideoHeight = 0
|
||||
}
|
||||
extension.numExtensions
|
||||
}
|
||||
|
||||
def notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
|
||||
def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
|
||||
def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
|
||||
def getDeskShareStarted(): Boolean = {
|
||||
return deskShareStarted
|
||||
}
|
||||
|
||||
def getMeetingExtensionProp(): MeetingExtensionProp = extension
|
||||
def muteMeeting() = meetingMuted = true
|
||||
def unmuteMeeting() = meetingMuted = false
|
||||
def isMeetingMuted(): Boolean = meetingMuted
|
||||
def recordingStarted() = recording = true
|
||||
def recordingStopped() = recording = false
|
||||
def isRecording(): Boolean = recording
|
||||
def lastWebUserLeft() = lastWebUserLeftOnTimestamp = timeNowInMinutes
|
||||
def lastWebUserLeftOn(): Long = lastWebUserLeftOnTimestamp
|
||||
def resetLastWebUserLeftOn() = lastWebUserLeftOnTimestamp = 0
|
||||
def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path
|
||||
def getVoiceRecordingFilename(): String = voiceRecordingFilename
|
||||
def permisionsInitialized(): Boolean = permissionsInited
|
||||
def initializePermissions() = permissionsInited = true
|
||||
def audioSettingsInitialized(): Boolean = audioSettingsInited
|
||||
def initializeAudioSettings() = audioSettingsInited = true
|
||||
def permissionsEqual(other: Permissions): Boolean = permissions == other
|
||||
def lockLayout(lock: Boolean) = permissions = permissions.copy(lockedLayout = lock)
|
||||
def getPermissions(): Permissions = permissions
|
||||
def setPermissions(p: Permissions) = permissions = p
|
||||
def meetingHasEnded() = meetingEnded = true
|
||||
def hasMeetingEnded(): Boolean = meetingEnded
|
||||
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
|
||||
def getGuestPolicy(): GuestPolicy.GuestPolicy = guestPolicy
|
||||
def setGuestPolicy(policy: GuestPolicy.GuestPolicy) = guestPolicy = policy
|
||||
def getGuestPolicySetBy(): String = guestPolicySetBy
|
||||
def setGuestPolicySetBy(user: String) = guestPolicySetBy = user
|
||||
def setDeskShareStarted(b: Boolean) {
|
||||
deskShareStarted = b
|
||||
}
|
||||
|
||||
def setDesktopShareVideoWidth(videoWidth: Int) {
|
||||
desktopShareVideoWidth = videoWidth
|
||||
}
|
||||
|
||||
def setDesktopShareVideoHeight(videoHeight: Int) {
|
||||
desktopShareVideoHeight = videoHeight
|
||||
}
|
||||
|
||||
def getDesktopShareVideoWidth(): Int = {
|
||||
desktopShareVideoWidth
|
||||
}
|
||||
|
||||
def getDesktopShareVideoHeight(): Int = {
|
||||
desktopShareVideoHeight
|
||||
}
|
||||
|
||||
def broadcastingRTMPStarted() {
|
||||
broadcastingRTMP = true
|
||||
}
|
||||
|
||||
def isBroadcastingRTMP(): Boolean = {
|
||||
broadcastingRTMP
|
||||
}
|
||||
|
||||
def broadcastingRTMPStopped() {
|
||||
broadcastingRTMP = false
|
||||
}
|
||||
|
||||
def setRTMPBroadcastingUrl(path: String) {
|
||||
rtmpBroadcastingUrl = path
|
||||
}
|
||||
|
||||
def getRTMPBroadcastingUrl(): String = {
|
||||
rtmpBroadcastingUrl
|
||||
}
|
||||
|
||||
def isExtensionAllowed(): Boolean = extension.numExtensions < extension.maxExtensions
|
||||
def incNumExtension(): Int = {
|
||||
if (extension.numExtensions < extension.maxExtensions) {
|
||||
extension = extension.copy(numExtensions = extension.numExtensions + 1); extension.numExtensions
|
||||
}
|
||||
extension.numExtensions
|
||||
}
|
||||
|
||||
def notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
|
||||
def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
|
||||
def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
|
||||
|
||||
def getMeetingExtensionProp(): MeetingExtensionProp = extension
|
||||
def muteMeeting() = meetingMuted = true
|
||||
def unmuteMeeting() = meetingMuted = false
|
||||
def isMeetingMuted(): Boolean = meetingMuted
|
||||
def recordingStarted() = recording = true
|
||||
def recordingStopped() = recording = false
|
||||
def isRecording(): Boolean = recording
|
||||
def lastWebUserLeft() = lastWebUserLeftOnTimestamp = timeNowInMinutes
|
||||
def lastWebUserLeftOn(): Long = lastWebUserLeftOnTimestamp
|
||||
def resetLastWebUserLeftOn() = lastWebUserLeftOnTimestamp = 0
|
||||
def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path
|
||||
def getVoiceRecordingFilename(): String = voiceRecordingFilename
|
||||
def permisionsInitialized(): Boolean = permissionsInited
|
||||
def initializePermissions() = permissionsInited = true
|
||||
def audioSettingsInitialized(): Boolean = audioSettingsInited
|
||||
def initializeAudioSettings() = audioSettingsInited = true
|
||||
def permissionsEqual(other: Permissions): Boolean = permissions == other
|
||||
def lockLayout(lock: Boolean) = permissions = permissions.copy(lockedLayout = lock)
|
||||
def getPermissions(): Permissions = permissions
|
||||
def setPermissions(p: Permissions) = permissions = p
|
||||
def meetingHasEnded() = meetingEnded = true
|
||||
def hasMeetingEnded(): Boolean = meetingEnded
|
||||
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
|
||||
def getGuestPolicy(): GuestPolicy.GuestPolicy = guestPolicy
|
||||
def setGuestPolicy(policy: GuestPolicy.GuestPolicy) = guestPolicy = policy
|
||||
def getGuestPolicySetBy(): String = guestPolicySetBy
|
||||
def setGuestPolicySetBy(user: String) = guestPolicySetBy = user
|
||||
*/
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.Props
|
||||
import akka.actor.OneForOneStrategy
|
||||
@ -20,8 +19,6 @@ import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
|
||||
import org.bigbluebutton.common.messages.PresentationRemovedMessage
|
||||
import org.bigbluebutton.common.messages.AllowUserToShareDesktopReply
|
||||
import org.bigbluebutton.core.apps.Page
|
||||
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.core.apps.SimplePollResultOutVO
|
||||
@ -38,7 +35,6 @@ import org.bigbluebutton.common.messages.LockLayoutMessage
|
||||
import org.bigbluebutton.core.pubsub.senders.WhiteboardMessageToJsonConverter
|
||||
import org.bigbluebutton.common.converters.ToJsonEncoder
|
||||
import org.bigbluebutton.common.messages.TransferUserToVoiceConfRequestMessage
|
||||
import org.bigbluebutton.core
|
||||
|
||||
object MessageSenderActor {
|
||||
def props(msgSender: MessageSender): Props =
|
||||
@ -705,6 +701,7 @@ class MessageSenderActor(val service: MessageSender)
|
||||
|
||||
private def handleUserJoined(msg: UserJoined) {
|
||||
val json = UsersMessageToJsonConverter.userJoinedToJson(msg)
|
||||
//println("************** Publishing [" + json + "] *******************")
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,23 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorContext
|
||||
import org.bigbluebutton.core.bus.OutgoingEventBus
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonOutMessage
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common2.messages.{ BbbCommonEnvCoreMsg }
|
||||
import org.bigbluebutton.core.bus.{ BbbOutMessage, BigBlueButtonOutMessage, OutEventBus2, OutgoingEventBus }
|
||||
import org.bigbluebutton.core.api.IOutMessage
|
||||
|
||||
object OutMessageGateway {
|
||||
def apply(outgoingEventBus: OutgoingEventBus) =
|
||||
new OutMessageGateway(outgoingEventBus)
|
||||
def apply(outgoingEventBus: OutgoingEventBus, outBus2: OutEventBus2) =
|
||||
new OutMessageGateway(outgoingEventBus, outBus2)
|
||||
}
|
||||
|
||||
class OutMessageGateway(outgoingEventBus: OutgoingEventBus) {
|
||||
class OutMessageGateway(outgoingEventBus: OutgoingEventBus, outBus2: OutEventBus2) extends SystemConfiguration {
|
||||
|
||||
def send(msg: IOutMessage) {
|
||||
outgoingEventBus.publish(BigBlueButtonOutMessage("outgoingMessageChannel", msg))
|
||||
outgoingEventBus.publish(BigBlueButtonOutMessage(outMessageChannel, msg))
|
||||
}
|
||||
}
|
||||
|
||||
def send(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
println("****** Sending to outBbbMsgMsgChannel " + msg.envelope.name)
|
||||
outBus2.publish(BbbOutMessage(outBbbMsgMsgChannel, msg))
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,16 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.Props
|
||||
import akka.actor.OneForOneStrategy
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.bigbluebutton.core.util._
|
||||
import org.bigbluebutton.core.recorder.RecorderActor
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp, CaptionApp }
|
||||
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel, CaptionModel }
|
||||
import org.bigbluebutton.core.apps.PresentationModel
|
||||
import org.bigbluebutton.core.service.recorder.RecorderApplication
|
||||
|
||||
object OutMessageGatewayActor {
|
||||
|
@ -4,7 +4,6 @@ import org.bigbluebutton.core.api.GuestPolicy._
|
||||
import org.bigbluebutton.core.api.SharedNotesOperation._
|
||||
import org.bigbluebutton.core.apps.AnnotationVO
|
||||
import org.bigbluebutton.core.apps.Presentation
|
||||
import org.bigbluebutton.core.MeetingProperties
|
||||
import org.bigbluebutton.core.apps.BreakoutUser
|
||||
import spray.json.JsObject
|
||||
|
||||
@ -34,7 +33,6 @@ case class KeepAliveMessage(aliveID: String) extends InMessage
|
||||
case class MonitorNumberOfUsers(meetingID: String) extends InMessage
|
||||
case class SendTimeRemainingUpdate(meetingId: String) extends InMessage
|
||||
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
|
||||
case class CreateMeeting(meetingID: String, mProps: MeetingProperties) extends InMessage
|
||||
case class InitializeMeeting(meetingID: String, recorded: Boolean) extends InMessage
|
||||
case class DestroyMeeting(meetingID: String) extends InMessage
|
||||
case class StartMeeting(meetingID: String) extends InMessage
|
||||
@ -103,7 +101,8 @@ case class LogoutEndMeeting(meetingID: String, userID: String) extends InMessage
|
||||
// Chat
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
|
||||
case class GetAllChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
|
||||
case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String, chatId: String) extends InMessage
|
||||
case class SendPublicMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
|
||||
case class SendPrivateMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
|
||||
case class ClearPublicChatHistoryRequest(meetingID: String, requesterID: String) extends InMessage
|
||||
@ -221,4 +220,4 @@ case class GetCurrentDocumentRequest(meetingID: String, requesterID: String) ext
|
||||
case class CreateAdditionalNotesRequest(meetingID: String, requesterID: String, noteName: String) extends InMessage
|
||||
case class DestroyAdditionalNotesRequest(meetingID: String, requesterID: String, noteID: String) extends InMessage
|
||||
case class RequestAdditionalNotesSetRequest(meetingID: String, requesterID: String, additionalNotesSetSize: Int) extends InMessage
|
||||
case class SharedNotesSyncNoteRequest(meetingID: String, requesterID: String, noteID: String) extends InMessage
|
||||
case class SharedNotesSyncNoteRequest(meetingID: String, requesterID: String, noteID: String) extends InMessage
|
||||
|
@ -11,7 +11,8 @@ import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.bus.IncomingEventBus
|
||||
import org.bigbluebutton.core.models.Users
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait BreakoutRoomApp extends SystemConfiguration {
|
||||
this: MeetingActor =>
|
||||
@ -22,18 +23,20 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
def handleBreakoutRoomsList(msg: BreakoutRoomsListMessage) {
|
||||
val breakoutRooms = liveMeeting.breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.externalMeetingId, r.id, r.sequence) }
|
||||
val roomsReady = liveMeeting.breakoutModel.pendingRoomsNumber == 0 && breakoutRooms.length > 0
|
||||
log.info("Sending breakout rooms list to {} with containing {} room(s)", mProps.meetingID, breakoutRooms.length)
|
||||
outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms, roomsReady))
|
||||
log.info("Sending breakout rooms list to {} with containing {} room(s)", props.meetingProp.intId, breakoutRooms.length)
|
||||
outGW.send(new BreakoutRoomsListOutMessage(props.meetingProp.intId, breakoutRooms, roomsReady))
|
||||
}
|
||||
|
||||
def handleCreateBreakoutRooms(msg: CreateBreakoutRooms) {
|
||||
// If breakout rooms are being created we ignore the coming message
|
||||
if (liveMeeting.breakoutModel.pendingRoomsNumber > 0) {
|
||||
log.warning("CreateBreakoutRooms event received while {} are pending to be created for meeting {}", liveMeeting.breakoutModel.pendingRoomsNumber, mProps.meetingID)
|
||||
log.warning("CreateBreakoutRooms event received while {} are pending to be created for meeting {}",
|
||||
liveMeeting.breakoutModel.pendingRoomsNumber, props.meetingProp.intId)
|
||||
return
|
||||
}
|
||||
if (liveMeeting.breakoutModel.getNumberOfRooms() > 0) {
|
||||
log.warning("CreateBreakoutRooms event received while {} breakout rooms running for meeting {}", liveMeeting.breakoutModel.getNumberOfRooms(), mProps.meetingID)
|
||||
log.warning("CreateBreakoutRooms event received while {} breakout rooms running for meeting {}",
|
||||
liveMeeting.breakoutModel.getNumberOfRooms(), props.meetingProp.intId)
|
||||
return
|
||||
}
|
||||
|
||||
@ -46,17 +49,18 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
|
||||
for (room <- msg.rooms) {
|
||||
i += 1
|
||||
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(mProps.meetingID, i)
|
||||
val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(mProps.voiceBridge, i)
|
||||
val r = liveMeeting.breakoutModel.createBreakoutRoom(mProps.meetingID, breakoutMeetingId._1, breakoutMeetingId._2, room.name,
|
||||
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(props.meetingProp.intId, i)
|
||||
val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(props.voiceProp.voiceConf, i)
|
||||
val r = liveMeeting.breakoutModel.createBreakoutRoom(props.meetingProp.intId, breakoutMeetingId._1, breakoutMeetingId._2, room.name,
|
||||
room.sequence, voiceConfId, room.users)
|
||||
val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID, r.sequence,
|
||||
r.voiceConfId, msg.durationInMinutes, mProps.moderatorPass, mProps.viewerPass,
|
||||
val p = new BreakoutRoomOutPayload(r.id, r.name, props.meetingProp.intId, r.sequence,
|
||||
r.voiceConfId, msg.durationInMinutes, props.password.moderatorPass, props.password.viewerPass,
|
||||
sourcePresentationId, sourcePresentationSlide, msg.record)
|
||||
outGW.send(new CreateBreakoutRoom(mProps.meetingID, p))
|
||||
outGW.send(new CreateBreakoutRoom(props.meetingProp.intId, p))
|
||||
}
|
||||
liveMeeting.meetingModel.breakoutRoomsdurationInMinutes = msg.durationInMinutes;
|
||||
liveMeeting.meetingModel.breakoutRoomsStartedOn = liveMeeting.timeNowInSeconds;
|
||||
|
||||
MeetingStatus2x.breakoutRoomsdurationInMinutes(liveMeeting.status, msg.durationInMinutes)
|
||||
MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status, MeetingStatus2x.timeNowInSeconds)
|
||||
}
|
||||
|
||||
def sendJoinURL(userId: String, externalMeetingId: String, roomSequence: String) {
|
||||
@ -64,14 +68,18 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
for {
|
||||
user <- Users.findWithId(userId, liveMeeting.users)
|
||||
apiCall = "join"
|
||||
params = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true, externalMeetingId, mProps.moderatorPass)
|
||||
params = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true,
|
||||
externalMeetingId, props.password.moderatorPass)
|
||||
// We generate a first url with redirect -> true
|
||||
redirectBaseString = BreakoutRoomsUtil.createBaseString(params._1)
|
||||
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString, BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret))
|
||||
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
|
||||
BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret))
|
||||
// We generate a second url with redirect -> false
|
||||
noRedirectBaseString = BreakoutRoomsUtil.createBaseString(params._2)
|
||||
noRedirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, noRedirectBaseString, BreakoutRoomsUtil.calculateChecksum(apiCall, noRedirectBaseString, bbbWebSharedSecret))
|
||||
} yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, externalMeetingId, userId, redirectJoinURL, noRedirectJoinURL))
|
||||
noRedirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, noRedirectBaseString,
|
||||
BreakoutRoomsUtil.calculateChecksum(apiCall, noRedirectBaseString, bbbWebSharedSecret))
|
||||
} yield outGW.send(new BreakoutRoomJoinURLOutMessage(props.meetingProp.intId,
|
||||
props.recordProp.record, externalMeetingId, userId, redirectJoinURL, noRedirectJoinURL))
|
||||
}
|
||||
|
||||
def handleRequestBreakoutJoinURL(msg: RequestBreakoutJoinURLInMessage) {
|
||||
@ -89,7 +97,7 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
|
||||
// We postpone sending invitation until all breakout rooms have been created
|
||||
if (liveMeeting.breakoutModel.pendingRoomsNumber == 0) {
|
||||
log.info("All breakout rooms created for meetingId={}", mProps.meetingID)
|
||||
log.info("All breakout rooms created for meetingId={}", props.meetingProp.intId)
|
||||
liveMeeting.breakoutModel.getRooms().foreach { room =>
|
||||
liveMeeting.breakoutModel.getAssignedUsers(room.id) foreach { users =>
|
||||
users.foreach { u =>
|
||||
@ -98,13 +106,15 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
}
|
||||
}
|
||||
}
|
||||
handleBreakoutRoomsList(new BreakoutRoomsListMessage(mProps.meetingID))
|
||||
handleBreakoutRoomsList(new BreakoutRoomsListMessage(props.meetingProp.intId))
|
||||
}
|
||||
}
|
||||
|
||||
def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, externalMeetingId: String, breakoutMeetingId: String, sequence: Int, voiceConfId: String) {
|
||||
def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, externalMeetingId: String,
|
||||
breakoutMeetingId: String, sequence: Int, voiceConfId: String) {
|
||||
log.info("Sending breakout room started {} for parent meeting {} ", breakoutMeetingId, meetingId);
|
||||
outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, externalMeetingId, breakoutMeetingId, sequence)))
|
||||
outGW.send(new BreakoutRoomStartedOutMessage(meetingId, props.recordProp.record, new BreakoutRoomBody(breakoutName,
|
||||
externalMeetingId, breakoutMeetingId, sequence)))
|
||||
}
|
||||
|
||||
def handleBreakoutRoomEnded(msg: BreakoutRoomEnded) {
|
||||
@ -114,21 +124,21 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
|
||||
def handleBreakoutRoomUsersUpdate(msg: BreakoutRoomUsersUpdate) {
|
||||
liveMeeting.breakoutModel.updateBreakoutUsers(msg.breakoutMeetingId, msg.users) foreach { room =>
|
||||
outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutMeetingId, room.users))
|
||||
outGW.send(new UpdateBreakoutUsersOutMessage(props.meetingProp.intId, props.recordProp.record, msg.breakoutMeetingId, room.users))
|
||||
}
|
||||
}
|
||||
|
||||
def handleSendBreakoutUsersUpdate(msg: SendBreakoutUsersUpdate) {
|
||||
val users = Users.getUsers(liveMeeting.users)
|
||||
val breakoutUsers = users map { u => new BreakoutUser(u.externalId, u.name) }
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.parentMeetingID,
|
||||
new BreakoutRoomUsersUpdate(mProps.parentMeetingID, mProps.meetingID, breakoutUsers)))
|
||||
eventBus.publish(BigBlueButtonEvent(props.breakoutProps.parentId,
|
||||
new BreakoutRoomUsersUpdate(props.breakoutProps.parentId, props.meetingProp.intId, breakoutUsers)))
|
||||
}
|
||||
|
||||
def handleTransferUserToMeeting(msg: TransferUserToMeetingRequest) {
|
||||
var targetVoiceBridge: String = msg.targetMeetingId
|
||||
// If the current room is a parent room we fetch the voice bridge from the breakout room
|
||||
if (!mProps.isBreakout) {
|
||||
if (!props.meetingProp.isBreakout) {
|
||||
liveMeeting.breakoutModel.getBreakoutRoom(msg.targetMeetingId) match {
|
||||
case Some(b) => {
|
||||
targetVoiceBridge = b.voiceConfId;
|
||||
@ -137,14 +147,14 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
}
|
||||
} // if it is a breakout room, the target voice bridge is the same after removing the last digit
|
||||
else {
|
||||
targetVoiceBridge = mProps.voiceBridge.dropRight(1)
|
||||
targetVoiceBridge = props.voiceProp.voiceConf.dropRight(1)
|
||||
}
|
||||
// We check the user from the mode
|
||||
Users.findWithId(msg.userId, liveMeeting.users) match {
|
||||
case Some(u) => {
|
||||
if (u.voiceUser.joined) {
|
||||
log.info("Transferring user userId=" + u.id + " from voiceBridge=" + mProps.voiceBridge + " to targetVoiceConf=" + targetVoiceBridge)
|
||||
outGW.send(new TransferUserToMeeting(mProps.voiceBridge, targetVoiceBridge, u.voiceUser.userId))
|
||||
log.info("Transferring user userId=" + u.id + " from voiceBridge=" + props.voiceProp.voiceConf + " to targetVoiceConf=" + targetVoiceBridge)
|
||||
outGW.send(new TransferUserToMeeting(props.voiceProp.voiceConf, targetVoiceBridge, u.voiceUser.userId))
|
||||
}
|
||||
}
|
||||
case None => // do nothing
|
||||
@ -152,7 +162,7 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
}
|
||||
|
||||
def handleEndAllBreakoutRooms(msg: EndAllBreakoutRooms) {
|
||||
log.info("EndAllBreakoutRooms event received for meetingId={}", mProps.meetingID)
|
||||
log.info("EndAllBreakoutRooms event received for meetingId={}", props.meetingProp.intId)
|
||||
liveMeeting.breakoutModel.getRooms().foreach { room =>
|
||||
outGW.send(new EndBreakoutRoom(room.id))
|
||||
}
|
||||
@ -174,7 +184,7 @@ object BreakoutRoomsUtil {
|
||||
}
|
||||
|
||||
def createJoinURL(webAPI: String, apiCall: String, baseString: String, checksum: String): String = {
|
||||
var apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/")
|
||||
val apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/")
|
||||
apiURL.concat(apiCall).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.running.{ MeetingActor }
|
||||
|
||||
@ -12,9 +10,9 @@ trait CaptionApp {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleSendCaptionHistoryRequest(msg: SendCaptionHistoryRequest) {
|
||||
var history = liveMeeting.captionModel.getHistory()
|
||||
val history = liveMeeting.captionModel.getHistory()
|
||||
//println("Caption history requested " + history)
|
||||
outGW.send(new SendCaptionHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID, history))
|
||||
outGW.send(new SendCaptionHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, history))
|
||||
}
|
||||
|
||||
def handleUpdateCaptionOwnerRequest(msg: UpdateCaptionOwnerRequest) {
|
||||
@ -24,7 +22,7 @@ trait CaptionApp {
|
||||
liveMeeting.captionModel.changeTranscriptOwner(t, "")
|
||||
|
||||
// send notification that owner has changed
|
||||
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
|
||||
outGW.send(new UpdateCaptionOwnerReply(props.meetingProp.intId, props.recordProp.record, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
|
||||
})
|
||||
}
|
||||
// create the locale if it doesn't exist
|
||||
@ -34,7 +32,7 @@ trait CaptionApp {
|
||||
liveMeeting.captionModel.newTranscript(msg.locale, msg.localeCode, msg.ownerID)
|
||||
}
|
||||
|
||||
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, msg.locale, msg.localeCode, msg.ownerID))
|
||||
outGW.send(new UpdateCaptionOwnerReply(props.meetingProp.intId, props.recordProp.record, msg.locale, msg.localeCode, msg.ownerID))
|
||||
}
|
||||
|
||||
def handleEditCaptionHistoryRequest(msg: EditCaptionHistoryRequest) {
|
||||
@ -42,7 +40,7 @@ trait CaptionApp {
|
||||
if (t == msg.locale) {
|
||||
liveMeeting.captionModel.editHistory(msg.startIndex, msg.endIndex, msg.locale, msg.text)
|
||||
|
||||
outGW.send(new EditCaptionHistoryReply(mProps.meetingID, mProps.recorded, msg.userID, msg.startIndex, msg.endIndex, msg.locale, msg.localeCode, msg.text))
|
||||
outGW.send(new EditCaptionHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.userID, msg.startIndex, msg.endIndex, msg.locale, msg.localeCode, msg.text))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -52,7 +50,7 @@ trait CaptionApp {
|
||||
liveMeeting.captionModel.changeTranscriptOwner(t, "")
|
||||
|
||||
// send notification that owner has changed
|
||||
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
|
||||
outGW.send(new UpdateCaptionOwnerReply(props.meetingProp.intId, props.recordProp.record, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
|
||||
})
|
||||
}
|
||||
}
|
@ -1,8 +1,5 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.immutable.HashMap
|
||||
|
||||
class CaptionModel {
|
||||
var transcripts = Map[String, Array[String]]()
|
||||
|
||||
@ -46,12 +43,12 @@ class CaptionModel {
|
||||
//println("editHistory entered")
|
||||
if (transcripts contains locale) {
|
||||
//println("editHistory found locale:" + locale)
|
||||
var oText: String = transcripts(locale)(1)
|
||||
val oText: String = transcripts(locale)(1)
|
||||
|
||||
if (startIndex >= 0 && endIndex <= oText.length && startIndex <= endIndex) {
|
||||
//println("editHistory passed index test")
|
||||
var sText: String = oText.substring(0, startIndex)
|
||||
var eText: String = oText.substring(endIndex)
|
||||
val sText: String = oText.substring(0, startIndex)
|
||||
val eText: String = oText.substring(endIndex)
|
||||
|
||||
transcripts(locale)(1) = (sText + text + eText)
|
||||
//println("editHistory new history is: " + transcripts(locale)(1))
|
||||
|
@ -1,36 +1,36 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.core.api._
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.{ OutMessageGateway }
|
||||
|
||||
trait ChatApp {
|
||||
this: MeetingActor =>
|
||||
|
||||
val props: DefaultProps
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleGetChatHistoryRequest(msg: GetChatHistoryRequest) {
|
||||
val history = liveMeeting.chatModel.getChatHistory()
|
||||
outGW.send(new GetChatHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.replyTo, history))
|
||||
outGW.send(new GetChatHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.replyTo, history))
|
||||
}
|
||||
|
||||
def handleSendPublicMessageRequest(msg: SendPublicMessageRequest) {
|
||||
liveMeeting.chatModel.addNewChatMessage(msg.message.toMap)
|
||||
val pubMsg = msg.message.toMap
|
||||
liveMeeting.chatModel.addNewChatMessage(msg.message)
|
||||
val pubMsg = msg.message
|
||||
|
||||
outGW.send(new SendPublicMessageEvent(mProps.meetingID, mProps.recorded, msg.requesterID, pubMsg))
|
||||
outGW.send(new SendPublicMessageEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, pubMsg))
|
||||
}
|
||||
|
||||
def handleSendPrivateMessageRequest(msg: SendPrivateMessageRequest) {
|
||||
val privMsg = msg.message.toMap
|
||||
outGW.send(new SendPrivateMessageEvent(mProps.meetingID, mProps.recorded, msg.requesterID, privMsg))
|
||||
val privMsg = msg.message
|
||||
outGW.send(new SendPrivateMessageEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, privMsg))
|
||||
}
|
||||
|
||||
def handleClearPublicChatHistoryRequest(msg: ClearPublicChatHistoryRequest) {
|
||||
liveMeeting.chatModel.clearPublicChatHistory()
|
||||
outGW.send(new ClearPublicChatHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID))
|
||||
outGW.send(new ClearPublicChatHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID))
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,15 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.models.{ DirectChats, PublicChats, UserIdAndName }
|
||||
import org.bigbluebutton.core.models.{ DirectChats, PublicChats }
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object ChatModel {
|
||||
def getChatHistory(chatModel: ChatModel): Array[Map[String, String]] = {
|
||||
chatModel.getChatHistory()
|
||||
}
|
||||
}
|
||||
|
||||
class ChatModel {
|
||||
|
||||
val directChats = new DirectChats
|
||||
|
@ -0,0 +1,19 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
trait ChatModelTrait {
|
||||
this: LiveMeeting =>
|
||||
|
||||
def getChatHistory(): Array[Map[String, String]] = {
|
||||
ChatModel.getChatHistory(chatModel)
|
||||
}
|
||||
|
||||
def addNewChatMessage(msg: Map[String, String]) = {
|
||||
chatModel.addNewChatMessage(msg)
|
||||
}
|
||||
|
||||
def clearPublicChatHistory(): Unit = {
|
||||
chatModel.clearPublicChatHistory()
|
||||
}
|
||||
}
|
@ -5,7 +5,8 @@ import org.bigbluebutton.core.api._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.models.{ Roles, UserVO, Users }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait LayoutApp {
|
||||
this: MeetingActor =>
|
||||
@ -13,15 +14,17 @@ trait LayoutApp {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleGetCurrentLayoutRequest(msg: GetCurrentLayoutRequest) {
|
||||
outGW.send(new GetCurrentLayoutReply(msg.meetingID, mProps.recorded, msg.requesterID,
|
||||
liveMeeting.layoutModel.getCurrentLayout(), liveMeeting.meetingModel.getPermissions().lockedLayout, liveMeeting.layoutModel.getLayoutSetter()))
|
||||
outGW.send(new GetCurrentLayoutReply(msg.meetingID, props.recordProp.record, msg.requesterID,
|
||||
liveMeeting.layoutModel.getCurrentLayout(),
|
||||
MeetingStatus2x.getPermissions(liveMeeting.status).lockedLayout,
|
||||
liveMeeting.layoutModel.getLayoutSetter()))
|
||||
}
|
||||
|
||||
def handleLockLayoutRequest(msg: LockLayoutRequest) {
|
||||
liveMeeting.layoutModel.applyToViewersOnly(msg.viewersOnly)
|
||||
liveMeeting.lockLayout(msg.lock)
|
||||
|
||||
outGW.send(new LockLayoutEvent(msg.meetingID, mProps.recorded, msg.setById, msg.lock, affectedUsers))
|
||||
outGW.send(new LockLayoutEvent(msg.meetingID, props.recordProp.record, msg.setById, msg.lock, affectedUsers))
|
||||
|
||||
msg.layout foreach { l =>
|
||||
liveMeeting.layoutModel.setCurrentLayout(l)
|
||||
@ -30,8 +33,10 @@ trait LayoutApp {
|
||||
}
|
||||
|
||||
private def broadcastSyncLayout(meetingId: String, setById: String) {
|
||||
outGW.send(new BroadcastLayoutEvent(meetingId, mProps.recorded, setById,
|
||||
liveMeeting.layoutModel.getCurrentLayout(), liveMeeting.meetingModel.getPermissions().lockedLayout, liveMeeting.layoutModel.getLayoutSetter(), affectedUsers))
|
||||
outGW.send(new BroadcastLayoutEvent(meetingId, props.recordProp.record, setById,
|
||||
liveMeeting.layoutModel.getCurrentLayout(),
|
||||
MeetingStatus2x.getPermissions(liveMeeting.status).lockedLayout,
|
||||
liveMeeting.layoutModel.getLayoutSetter(), affectedUsers))
|
||||
}
|
||||
|
||||
def handleBroadcastLayoutRequest(msg: BroadcastLayoutRequest) {
|
||||
@ -40,9 +45,9 @@ trait LayoutApp {
|
||||
}
|
||||
|
||||
def handleLockLayout(lock: Boolean, setById: String) {
|
||||
outGW.send(new LockLayoutEvent(mProps.meetingID, mProps.recorded, setById, lock, affectedUsers))
|
||||
outGW.send(new LockLayoutEvent(props.meetingProp.intId, props.recordProp.record, setById, lock, affectedUsers))
|
||||
|
||||
broadcastSyncLayout(mProps.meetingID, setById)
|
||||
broadcastSyncLayout(props.meetingProp.intId, setById)
|
||||
}
|
||||
|
||||
def affectedUsers(): Array[UserVO] = {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import scala.collection.mutable.HashMap
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object PollType {
|
||||
@ -42,7 +41,7 @@ object PollFactory {
|
||||
|
||||
if (numQs > 0 && numQs <= 6) {
|
||||
val answers = new Array[Answer](numQs)
|
||||
var i = 0
|
||||
|
||||
for (i <- 0 until numQs) {
|
||||
answers(i) = new Answer(i, LetterArray(i), Some(LetterArray(i)))
|
||||
val question = new Question(0, PollType.LetterPollType, multiResponse, None, answers)
|
||||
|
@ -28,13 +28,13 @@ trait PollApp {
|
||||
poll match {
|
||||
case Some(p) => {
|
||||
if (p.started && p.stopped && p.showResult) {
|
||||
outGW.send(new GetCurrentPollReplyMessage(mProps.meetingID, mProps.recorded, msg.requesterId, true, Some(p)))
|
||||
outGW.send(new GetCurrentPollReplyMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, true, Some(p)))
|
||||
} else {
|
||||
outGW.send(new GetCurrentPollReplyMessage(mProps.meetingID, mProps.recorded, msg.requesterId, false, None))
|
||||
outGW.send(new GetCurrentPollReplyMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, false, None))
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
outGW.send(new GetCurrentPollReplyMessage(mProps.meetingID, mProps.recorded, msg.requesterId, false, None))
|
||||
outGW.send(new GetCurrentPollReplyMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, false, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,7 +50,7 @@ trait PollApp {
|
||||
poll <- PollModel.getPoll(msg.pollId, liveMeeting.pollModel)
|
||||
} yield {
|
||||
PollModel.hidePollResult(msg.pollId, liveMeeting.pollModel)
|
||||
outGW.send(new PollHideResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId))
|
||||
outGW.send(new PollHideResultMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, msg.pollId))
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ trait PollApp {
|
||||
page <- liveMeeting.presModel.getCurrentPage()
|
||||
pageId = if (poll.id.contains("deskshare")) "deskshare" else page.id
|
||||
annotation = new AnnotationVO(poll.id, WhiteboardKeyUtil.DRAW_END_STATUS, WhiteboardKeyUtil.POLL_RESULT_TYPE, shape, page.id, msg.requesterId, -1)
|
||||
} handleSendWhiteboardAnnotationRequest(new SendWhiteboardAnnotationRequest(mProps.meetingID, msg.requesterId, annotation))
|
||||
} handleSendWhiteboardAnnotationRequest(new SendWhiteboardAnnotationRequest(props.meetingProp.intId, msg.requesterId, annotation))
|
||||
}
|
||||
|
||||
for {
|
||||
@ -102,7 +102,7 @@ trait PollApp {
|
||||
} yield {
|
||||
send(poll, shape)
|
||||
PollModel.showPollResult(msg.pollId, liveMeeting.pollModel)
|
||||
outGW.send(new PollShowResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId, poll))
|
||||
outGW.send(new PollShowResultMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, msg.pollId, poll))
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ trait PollApp {
|
||||
curPoll <- PollModel.getRunningPollThatStartsWith(page.id, liveMeeting.pollModel)
|
||||
} yield {
|
||||
PollModel.stopPoll(curPoll.id, liveMeeting.pollModel)
|
||||
outGW.send(new PollStoppedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, curPoll.id))
|
||||
outGW.send(new PollStoppedMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, curPoll.id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ trait PollApp {
|
||||
simplePoll <- PollModel.getSimplePoll(pollId, liveMeeting.pollModel)
|
||||
} yield {
|
||||
PollModel.startPoll(poll.id, liveMeeting.pollModel)
|
||||
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, simplePoll))
|
||||
outGW.send(new PollStartedMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, pollId, simplePoll))
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ trait PollApp {
|
||||
simplePoll <- PollModel.getSimplePoll(pollId, liveMeeting.pollModel)
|
||||
} yield {
|
||||
PollModel.startPoll(poll.id, liveMeeting.pollModel)
|
||||
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, simplePoll))
|
||||
outGW.send(new PollStartedMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, pollId, simplePoll))
|
||||
}
|
||||
|
||||
}
|
||||
@ -188,7 +188,7 @@ trait PollApp {
|
||||
responder = new Responder(user.id, user.name)
|
||||
updatedPoll <- storePollResult(responder)
|
||||
curPres <- Users.getCurrentPresenter(liveMeeting.users)
|
||||
} yield outGW.send(new UserRespondedToPollMessage(mProps.meetingID, mProps.recorded, curPres.id, msg.pollId, updatedPoll))
|
||||
} yield outGW.send(new UserRespondedToPollMessage(props.meetingProp.intId, props.recordProp.record, curPres.id, msg.pollId, updatedPoll))
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import com.google.gson.Gson
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.models.Users
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait PresentationApp {
|
||||
this: MeetingActor =>
|
||||
@ -26,23 +26,23 @@ trait PresentationApp {
|
||||
}
|
||||
|
||||
def handleClearPresentation(msg: ClearPresentation) {
|
||||
outGW.send(new ClearPresentationOutMsg(mProps.meetingID, mProps.recorded))
|
||||
outGW.send(new ClearPresentationOutMsg(props.meetingProp.intId, props.recordProp.record))
|
||||
}
|
||||
|
||||
def handlePresentationConversionUpdate(msg: PresentationConversionUpdate) {
|
||||
outGW.send(new PresentationConversionProgress(mProps.meetingID, msg.messageKey,
|
||||
outGW.send(new PresentationConversionProgress(props.meetingProp.intId, msg.messageKey,
|
||||
msg.code, msg.presentationId, msg.presName))
|
||||
}
|
||||
|
||||
def handlePresentationPageCountError(msg: PresentationPageCountError) {
|
||||
outGW.send(new PresentationConversionError(mProps.meetingID, msg.messageKey,
|
||||
outGW.send(new PresentationConversionError(props.meetingProp.intId, msg.messageKey,
|
||||
msg.code, msg.presentationId,
|
||||
msg.numberOfPages,
|
||||
msg.maxNumberPages, msg.presName))
|
||||
}
|
||||
|
||||
def handlePresentationSlideGenerated(msg: PresentationSlideGenerated) {
|
||||
outGW.send(new PresentationPageGenerated(mProps.meetingID, msg.messageKey,
|
||||
outGW.send(new PresentationPageGenerated(props.meetingProp.intId, msg.messageKey,
|
||||
msg.code, msg.presentationId,
|
||||
msg.numberOfPages,
|
||||
msg.pagesCompleted, msg.presName))
|
||||
@ -52,7 +52,7 @@ trait PresentationApp {
|
||||
|
||||
liveMeeting.presModel.addPresentation(msg.presentation)
|
||||
|
||||
outGW.send(new PresentationConversionDone(mProps.meetingID, mProps.recorded, msg.messageKey,
|
||||
outGW.send(new PresentationConversionDone(props.meetingProp.intId, props.recordProp.record, msg.messageKey,
|
||||
msg.code, msg.presentation))
|
||||
|
||||
sharePresentation(msg.presentation.id, true)
|
||||
@ -69,16 +69,16 @@ trait PresentationApp {
|
||||
}
|
||||
})
|
||||
|
||||
outGW.send(new RemovePresentationOutMsg(msg.meetingID, mProps.recorded, msg.presentationID))
|
||||
outGW.send(new RemovePresentationOutMsg(msg.meetingID, props.recordProp.record, msg.presentationID))
|
||||
|
||||
}
|
||||
|
||||
def handleGetPresentationInfo(msg: GetPresentationInfo) {
|
||||
val curPresenter = liveMeeting.getCurrentPresenterInfo()
|
||||
val curPresenter = MeetingStatus2x.getCurrentPresenterInfo(liveMeeting.status)
|
||||
val presenter = new CurrentPresenter(curPresenter.presenterID, curPresenter.presenterName, curPresenter.assignedBy)
|
||||
val presentations = liveMeeting.presModel.getPresentations
|
||||
val presentationInfo = new CurrentPresentationInfo(presenter, presentations)
|
||||
outGW.send(new GetPresentationInfoOutMsg(mProps.meetingID, mProps.recorded, msg.requesterID, presentationInfo, msg.replyTo))
|
||||
outGW.send(new GetPresentationInfoOutMsg(props.meetingProp.intId, props.recordProp.record, msg.requesterID, presentationInfo, msg.replyTo))
|
||||
}
|
||||
|
||||
def handleResizeAndMoveSlide(msg: ResizeAndMoveSlide) {
|
||||
@ -89,17 +89,17 @@ trait PresentationApp {
|
||||
val height = if (msg.heightRatio <= 100) msg.heightRatio else 100
|
||||
|
||||
val page = liveMeeting.presModel.resizePage(xOffset, yOffset, width, height);
|
||||
page foreach (p => outGW.send(new ResizeAndMoveSlideOutMsg(mProps.meetingID, mProps.recorded, p)))
|
||||
page foreach (p => outGW.send(new ResizeAndMoveSlideOutMsg(props.meetingProp.intId, props.recordProp.record, p)))
|
||||
}
|
||||
|
||||
def handleGotoSlide(msg: GotoSlide) {
|
||||
liveMeeting.presModel.changePage(msg.page) foreach { page =>
|
||||
log.debug("Switching page for meeting=[{}] page=[{}]", msg.meetingID, page.num);
|
||||
outGW.send(new GotoSlideOutMsg(mProps.meetingID, mProps.recorded, page))
|
||||
outGW.send(new GotoSlideOutMsg(props.meetingProp.intId, props.recordProp.record, page))
|
||||
}
|
||||
|
||||
Users.getCurrentPresenter(liveMeeting.users) foreach { pres =>
|
||||
handleStopPollRequest(StopPollRequest(mProps.meetingID, pres.id))
|
||||
handleStopPollRequest(StopPollRequest(props.meetingProp.intId, pres.id))
|
||||
}
|
||||
|
||||
}
|
||||
@ -112,10 +112,10 @@ trait PresentationApp {
|
||||
val pres = liveMeeting.presModel.sharePresentation(presentationID)
|
||||
|
||||
pres foreach { p =>
|
||||
outGW.send(new SharePresentationOutMsg(mProps.meetingID, mProps.recorded, p))
|
||||
outGW.send(new SharePresentationOutMsg(props.meetingProp.intId, props.recordProp.record, p))
|
||||
|
||||
liveMeeting.presModel.getCurrentPage(p) foreach { page =>
|
||||
outGW.send(new GotoSlideOutMsg(mProps.meetingID, mProps.recorded, page))
|
||||
outGW.send(new GotoSlideOutMsg(props.meetingProp.intId, props.recordProp.record, page))
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ trait PresentationApp {
|
||||
def handleGetSlideInfo(msg: GetSlideInfo) {
|
||||
liveMeeting.presModel.getCurrentPresentation foreach { pres =>
|
||||
liveMeeting.presModel.getCurrentPage(pres) foreach { page =>
|
||||
outGW.send(new GetSlideInfoOutMsg(mProps.meetingID, mProps.recorded, msg.requesterID, page, msg.replyTo))
|
||||
outGW.send(new GetSlideInfoOutMsg(props.meetingProp.intId, props.recordProp.record, msg.requesterID, page, msg.replyTo))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor }
|
||||
|
||||
trait SharedNotesApp {
|
||||
this: MeetingActor =>
|
||||
@ -19,20 +19,20 @@ trait SharedNotesApp {
|
||||
|
||||
val (patchID, patch, undo, redo) = liveMeeting.notesModel.patchDocument(msg.noteID, msg.patch, msg.operation)
|
||||
|
||||
if (patch != "") outGW.send(new PatchDocumentReply(mProps.meetingID, mProps.recorded, requesterID, msg.noteID, patch, patchID, undo, redo))
|
||||
if (patch != "") outGW.send(new PatchDocumentReply(props.meetingProp.intId, props.recordProp.record, requesterID, msg.noteID, patch, patchID, undo, redo))
|
||||
}
|
||||
|
||||
def handleGetCurrentDocumentRequest(msg: GetCurrentDocumentRequest) {
|
||||
val notesReport = liveMeeting.notesModel.notesReport.toMap
|
||||
|
||||
outGW.send(new GetCurrentDocumentReply(mProps.meetingID, mProps.recorded, msg.requesterID, notesReport))
|
||||
outGW.send(new GetCurrentDocumentReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, notesReport))
|
||||
}
|
||||
|
||||
private def createAdditionalNotes(requesterID: String, noteName: String = "") {
|
||||
liveMeeting.notesModel.synchronized {
|
||||
val noteID = liveMeeting.notesModel.createNote(noteName)
|
||||
|
||||
outGW.send(new CreateAdditionalNotesReply(mProps.meetingID, mProps.recorded, requesterID, noteID, noteName))
|
||||
outGW.send(new CreateAdditionalNotesReply(props.meetingProp.intId, props.recordProp.record, requesterID, noteID, noteName))
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ trait SharedNotesApp {
|
||||
liveMeeting.notesModel.synchronized {
|
||||
liveMeeting.notesModel.destroyNote(msg.noteID)
|
||||
|
||||
outGW.send(new DestroyAdditionalNotesReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.noteID))
|
||||
outGW.send(new DestroyAdditionalNotesReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.noteID))
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ trait SharedNotesApp {
|
||||
|
||||
def handleSharedNotesSyncNoteRequest(msg: SharedNotesSyncNoteRequest) {
|
||||
liveMeeting.notesModel.getNoteReport(msg.noteID) match {
|
||||
case Some(note) => outGW.send(new SharedNotesSyncNoteReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.noteID, note))
|
||||
case Some(note) => outGW.send(new SharedNotesSyncNoteReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.noteID, note))
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
|
2
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/SharedNotesModel.scala
Normal file → Executable file
2
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/SharedNotesModel.scala
Normal file → Executable file
@ -3,11 +3,9 @@ package org.bigbluebutton.core.apps
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.api.SharedNotesOperation._
|
||||
import name.fraser.neil.plaintext.diff_match_patch
|
||||
import name.fraser.neil.plaintext.diff_match_patch._
|
||||
import scala.collection.mutable.Stack
|
||||
import scala.collection.mutable.HashMap
|
||||
import scala.collection._
|
||||
import java.util.Collections
|
||||
|
||||
class SharedNotesModel {
|
||||
val notes = new HashMap[String, Note]()
|
||||
|
@ -1,11 +1,11 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import scala.collection.immutable.ListSet
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.api.GuestPolicy
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait UsersApp {
|
||||
this: MeetingActor =>
|
||||
@ -13,40 +13,40 @@ trait UsersApp {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleUserConnectedToGlobalAudio(msg: UserConnectedToGlobalAudio) {
|
||||
log.info("Handling UserConnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + msg.userid)
|
||||
log.info("Handling UserConnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + msg.userid)
|
||||
|
||||
val user = Users.findWithId(msg.userid, liveMeeting.users)
|
||||
user foreach { u =>
|
||||
if (liveMeeting.addGlobalAudioConnection(msg.userid)) {
|
||||
if (MeetingStatus2x.addGlobalAudioConnection(liveMeeting.status, msg.userid)) {
|
||||
for {
|
||||
uvo <- Users.joinedVoiceListenOnly(msg.userid, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("UserConnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
|
||||
outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.id, uvo.listenOnly))
|
||||
log.info("UserConnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
|
||||
outGW.send(new UserListeningOnly(props.meetingProp.intId, props.recordProp.record, uvo.id, uvo.listenOnly))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleUserDisconnectedFromGlobalAudio(msg: UserDisconnectedFromGlobalAudio) {
|
||||
log.info("Handling UserDisconnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + msg.userid)
|
||||
log.info("Handling UserDisconnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + msg.userid)
|
||||
|
||||
val user = Users.findWithId(msg.userid, liveMeeting.users)
|
||||
user foreach { u =>
|
||||
if (liveMeeting.removeGlobalAudioConnection(msg.userid)) {
|
||||
if (MeetingStatus2x.removeGlobalAudioConnection(liveMeeting.status, msg.userid)) {
|
||||
if (!u.joinedWeb) {
|
||||
for {
|
||||
uvo <- Users.userLeft(u.id, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("Not web user. Send user left message. meetingId=" + mProps.meetingID + " userId=" + u.id + " user=" + u)
|
||||
outGW.send(new UserLeft(mProps.meetingID, mProps.recorded, uvo))
|
||||
log.info("Not web user. Send user left message. meetingId=" + props.meetingProp.intId + " userId=" + u.id + " user=" + u)
|
||||
outGW.send(new UserLeft(props.meetingProp.intId, props.recordProp.record, uvo))
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
uvo <- Users.leftVoiceListenOnly(u.id, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("UserDisconnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
|
||||
outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.id, uvo.listenOnly))
|
||||
log.info("UserDisconnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
|
||||
outGW.send(new UserListeningOnly(props.meetingProp.intId, props.recordProp.record, uvo.id, uvo.listenOnly))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,28 +55,30 @@ trait UsersApp {
|
||||
|
||||
def handleMuteAllExceptPresenterRequest(msg: MuteAllExceptPresenterRequest) {
|
||||
if (msg.mute) {
|
||||
liveMeeting.meetingModel.muteMeeting()
|
||||
MeetingStatus2x.muteMeeting(liveMeeting.status)
|
||||
} else {
|
||||
liveMeeting.meetingModel.unmuteMeeting()
|
||||
MeetingStatus2x.unmuteMeeting(liveMeeting.status)
|
||||
}
|
||||
outGW.send(new MeetingMuted(mProps.meetingID, mProps.recorded, liveMeeting.meetingModel.isMeetingMuted()))
|
||||
outGW.send(new MeetingMuted(props.meetingProp.intId, props.recordProp.record,
|
||||
MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
|
||||
|
||||
usersWhoAreNotPresenter foreach { u =>
|
||||
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded, msg.requesterID,
|
||||
u.id, mProps.voiceBridge, u.voiceUser.userId, msg.mute))
|
||||
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record, msg.requesterID,
|
||||
u.id, props.voiceProp.voiceConf, u.voiceUser.userId, msg.mute))
|
||||
}
|
||||
}
|
||||
|
||||
def handleMuteMeetingRequest(msg: MuteMeetingRequest) {
|
||||
if (msg.mute) {
|
||||
liveMeeting.meetingModel.muteMeeting()
|
||||
MeetingStatus2x.muteMeeting(liveMeeting.status)
|
||||
} else {
|
||||
liveMeeting.meetingModel.unmuteMeeting()
|
||||
MeetingStatus2x.unmuteMeeting(liveMeeting.status)
|
||||
}
|
||||
outGW.send(new MeetingMuted(mProps.meetingID, mProps.recorded, liveMeeting.meetingModel.isMeetingMuted()))
|
||||
outGW.send(new MeetingMuted(props.meetingProp.intId, props.recordProp.record,
|
||||
MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
|
||||
Users.getUsers(liveMeeting.users) foreach { u =>
|
||||
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded, msg.requesterID,
|
||||
u.id, mProps.voiceBridge, u.voiceUser.userId, msg.mute))
|
||||
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record, msg.requesterID,
|
||||
u.id, props.voiceProp.voiceConf, u.voiceUser.userId, msg.mute))
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,49 +86,48 @@ trait UsersApp {
|
||||
log.info("Got ValidateAuthToken message. meetingId=" + msg.meetingID + " userId=" + msg.userId)
|
||||
RegisteredUsers.getRegisteredUserWithToken(msg.token, msg.userId, liveMeeting.registeredUsers) match {
|
||||
case Some(u) =>
|
||||
val replyTo = mProps.meetingID + '/' + msg.userId
|
||||
|
||||
//send the reply
|
||||
outGW.send(new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, true, msg.correlationId))
|
||||
outGW.send(new ValidateAuthTokenReply(props.meetingProp.intId, msg.userId, msg.token, true, msg.correlationId))
|
||||
|
||||
log.info("ValidateToken success. meetingId=" + mProps.meetingID + " userId=" + msg.userId)
|
||||
log.info("ValidateToken success. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId)
|
||||
|
||||
//join the user
|
||||
handleUserJoin(new UserJoining(mProps.meetingID, msg.userId, msg.token))
|
||||
handleUserJoin(new UserJoining(props.meetingProp.intId, msg.userId, msg.token))
|
||||
case None =>
|
||||
log.info("ValidateToken failed. meetingId=" + mProps.meetingID + " userId=" + msg.userId)
|
||||
outGW.send(new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, false, msg.correlationId))
|
||||
log.info("ValidateToken failed. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId)
|
||||
outGW.send(new ValidateAuthTokenReply(props.meetingProp.intId, msg.userId, msg.token, false, msg.correlationId))
|
||||
}
|
||||
}
|
||||
|
||||
def handleRegisterUser(msg: RegisterUser) {
|
||||
if (liveMeeting.meetingModel.hasMeetingEnded()) {
|
||||
if (MeetingStatus2x.hasMeetingEnded(liveMeeting.status)) {
|
||||
// Check first if the meeting has ended and the user refreshed the client to re-connect.
|
||||
log.info("Register user failed. Mmeeting has ended. meetingId=" + mProps.meetingID + " userId=" + msg.userID)
|
||||
log.info("Register user failed. Mmeeting has ended. meetingId=" + props.meetingProp.intId + " userId=" + msg.userID)
|
||||
sendMeetingHasEnded(msg.userID)
|
||||
} else {
|
||||
val regUser = RegisteredUsers.create(msg.userID, msg.extUserID, msg.name, msg.role, msg.authToken,
|
||||
msg.avatarURL, msg.guest, msg.authed, msg.guest, liveMeeting.registeredUsers)
|
||||
|
||||
log.info("Register user success. meetingId=" + mProps.meetingID + " userId=" + msg.userID + " user=" + regUser)
|
||||
outGW.send(new UserRegistered(mProps.meetingID, mProps.recorded, regUser))
|
||||
log.info("Register user success. meetingId=" + props.meetingProp.intId + " userId=" + msg.userID + " user=" + regUser)
|
||||
outGW.send(new UserRegistered(props.meetingProp.intId, props.recordProp.record, regUser))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def handleIsMeetingMutedRequest(msg: IsMeetingMutedRequest) {
|
||||
outGW.send(new IsMeetingMutedReply(mProps.meetingID, mProps.recorded,
|
||||
msg.requesterID, liveMeeting.meetingModel.isMeetingMuted()))
|
||||
outGW.send(new IsMeetingMutedReply(props.meetingProp.intId, props.recordProp.record,
|
||||
msg.requesterID, MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
|
||||
}
|
||||
|
||||
def handleMuteUserRequest(msg: MuteUserRequest) {
|
||||
log.info("Received mute user request. meetingId=" + mProps.meetingID + " userId=" + msg.userID + " mute=" + msg.mute)
|
||||
log.info("Received mute user request. meetingId=" + props.meetingProp.intId + " userId=" + msg.userID + " mute=" + msg.mute)
|
||||
for {
|
||||
u <- Users.findWithId(msg.userID, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("Send mute user request. meetingId=" + mProps.meetingID + " userId=" + u.id + " user=" + u)
|
||||
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded,
|
||||
msg.requesterID, u.id, mProps.voiceBridge, u.voiceUser.userId, msg.mute))
|
||||
log.info("Send mute user request. meetingId=" + props.meetingProp.intId + " userId=" + u.id + " user=" + u)
|
||||
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
|
||||
msg.requesterID, u.id, props.voiceProp.voiceConf, u.voiceUser.userId, msg.mute))
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,8 +138,9 @@ trait UsersApp {
|
||||
u <- Users.findWithId(msg.userId, liveMeeting.users)
|
||||
} yield {
|
||||
if (u.voiceUser.joined) {
|
||||
log.info("Ejecting user from voice. meetingId=" + mProps.meetingID + " userId=" + u.id)
|
||||
outGW.send(new EjectVoiceUser(mProps.meetingID, mProps.recorded, msg.ejectedBy, u.id, mProps.voiceBridge, u.voiceUser.userId))
|
||||
log.info("Ejecting user from voice. meetingId=" + props.meetingProp.intId + " userId=" + u.id)
|
||||
outGW.send(new EjectVoiceUser(props.meetingProp.intId, props.recordProp.record, msg.ejectedBy, u.id,
|
||||
props.voiceProp.voiceConf, u.voiceUser.userId))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,15 +149,17 @@ trait UsersApp {
|
||||
//println("*************** Reply with current lock settings ********************")
|
||||
|
||||
//reusing the existing handle for NewPermissionsSettings to reply to the GetLockSettings request
|
||||
outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.userId,
|
||||
liveMeeting.meetingModel.getPermissions(), Users.getUsers(liveMeeting.users).toArray))
|
||||
outGW.send(new NewPermissionsSetting(props.meetingProp.intId, msg.userId,
|
||||
MeetingStatus2x.getPermissions(liveMeeting.status),
|
||||
Users.getUsers(liveMeeting.users).toArray))
|
||||
}
|
||||
|
||||
def handleSetLockSettings(msg: SetLockSettings) {
|
||||
if (!liveMeeting.permissionsEqual(msg.settings)) {
|
||||
liveMeeting.newPermissions(msg.settings)
|
||||
outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.setByUser,
|
||||
liveMeeting.meetingModel.getPermissions, Users.getUsers(liveMeeting.users).toArray))
|
||||
outGW.send(new NewPermissionsSetting(props.meetingProp.intId, msg.setByUser,
|
||||
MeetingStatus2x.getPermissions(liveMeeting.status),
|
||||
Users.getUsers(liveMeeting.users).toArray))
|
||||
|
||||
handleLockLayout(msg.settings.lockedLayout, msg.setByUser)
|
||||
}
|
||||
@ -165,26 +169,27 @@ trait UsersApp {
|
||||
for {
|
||||
uvo <- Users.lockUser(msg.userID, msg.lock, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("Lock user. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " locked=" + uvo.locked)
|
||||
outGW.send(new UserLocked(mProps.meetingID, uvo.id, uvo.locked))
|
||||
log.info("Lock user. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " locked=" + uvo.locked)
|
||||
outGW.send(new UserLocked(props.meetingProp.intId, uvo.id, uvo.locked))
|
||||
}
|
||||
}
|
||||
|
||||
def handleInitLockSettings(msg: InitLockSettings) {
|
||||
if (!liveMeeting.meetingModel.permisionsInitialized()) {
|
||||
liveMeeting.meetingModel.initializePermissions()
|
||||
if (!MeetingStatus2x.permisionsInitialized(liveMeeting.status)) {
|
||||
MeetingStatus2x.initializePermissions(liveMeeting.status)
|
||||
liveMeeting.newPermissions(msg.settings)
|
||||
outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.settings, Users.getUsers(liveMeeting.users).toArray))
|
||||
outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.settings,
|
||||
Users.getUsers(liveMeeting.users).toArray))
|
||||
}
|
||||
}
|
||||
|
||||
def handleInitAudioSettings(msg: InitAudioSettings) {
|
||||
if (!liveMeeting.meetingModel.audioSettingsInitialized()) {
|
||||
liveMeeting.meetingModel.initializeAudioSettings()
|
||||
if (!MeetingStatus2x.audioSettingsInitialized(liveMeeting.status)) {
|
||||
MeetingStatus2x.initializeAudioSettings(liveMeeting.status)
|
||||
|
||||
if (liveMeeting.meetingModel.isMeetingMuted() != msg.muted) {
|
||||
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status) != msg.muted) {
|
||||
handleMuteAllExceptPresenterRequest(
|
||||
new MuteAllExceptPresenterRequest(mProps.meetingID,
|
||||
new MuteAllExceptPresenterRequest(props.meetingProp.intId,
|
||||
msg.requesterID, msg.muted));
|
||||
}
|
||||
}
|
||||
@ -198,7 +203,7 @@ trait UsersApp {
|
||||
for {
|
||||
uvo <- Users.setEmojiStatus(msg.userId, liveMeeting.users, msg.emojiStatus)
|
||||
} yield {
|
||||
outGW.send(new UserChangedEmojiStatus(mProps.meetingID, mProps.recorded, msg.emojiStatus, uvo.id))
|
||||
outGW.send(new UserChangedEmojiStatus(props.meetingProp.intId, props.recordProp.record, msg.emojiStatus, uvo.id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,7 +213,7 @@ trait UsersApp {
|
||||
} yield {
|
||||
RegisteredUsers.updateRegUser(uvo, liveMeeting.registeredUsers)
|
||||
val userRole = if (msg.role == Roles.MODERATOR_ROLE) "MODERATOR" else "VIEWER"
|
||||
outGW.send(new UserRoleChange(mProps.meetingID, mProps.recorded, msg.userID, userRole))
|
||||
outGW.send(new UserRoleChange(props.meetingProp.intId, props.recordProp.record, msg.userID, userRole))
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,21 +225,23 @@ trait UsersApp {
|
||||
*/
|
||||
val moderator = Users.findAModerator(liveMeeting.users)
|
||||
moderator.foreach { mod =>
|
||||
log.info("Presenter left meeting. meetingId=" + mProps.meetingID + " userId=" + user.id
|
||||
log.info("Presenter left meeting. meetingId=" + props.meetingProp.intId + " userId=" + user.id
|
||||
+ ". Making user=[" + mod.id + "] presenter.")
|
||||
assignNewPresenter(mod.id, mod.name, mod.id)
|
||||
}
|
||||
|
||||
if (liveMeeting.meetingModel.isBroadcastingRTMP()) {
|
||||
if (MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
|
||||
// The presenter left during desktop sharing. Stop desktop sharing on FreeSWITCH
|
||||
outGW.send(new DeskShareHangUp(mProps.meetingID, mProps.voiceBridge))
|
||||
outGW.send(new DeskShareHangUp(props.meetingProp.intId, props.voiceProp.voiceConf))
|
||||
|
||||
// notify other clients to close their deskshare view
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, liveMeeting.meetingModel.getRTMPBroadcastingUrl(),
|
||||
liveMeeting.meetingModel.getDesktopShareVideoWidth(), liveMeeting.meetingModel.getDesktopShareVideoHeight(), false))
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(props.meetingProp.intId,
|
||||
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status),
|
||||
MeetingStatus2x.getDesktopShareVideoWidth(liveMeeting.status),
|
||||
MeetingStatus2x.getDesktopShareVideoHeight(liveMeeting.status), false))
|
||||
|
||||
// reset meeting info
|
||||
liveMeeting.meetingModel.resetDesktopSharingParams()
|
||||
MeetingStatus2x.resetDesktopSharingParams(liveMeeting.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,17 +251,17 @@ trait UsersApp {
|
||||
user <- Users.userLeft(msg.userId, liveMeeting.users)
|
||||
} yield {
|
||||
if (user.voiceUser.joined) {
|
||||
outGW.send(new EjectVoiceUser(mProps.meetingID, mProps.recorded,
|
||||
msg.ejectedBy, msg.userId, mProps.voiceBridge, user.voiceUser.userId))
|
||||
outGW.send(new EjectVoiceUser(props.meetingProp.intId, props.recordProp.record,
|
||||
msg.ejectedBy, msg.userId, props.voiceProp.voiceConf, user.voiceUser.userId))
|
||||
}
|
||||
RegisteredUsers.remove(msg.userId, liveMeeting.registeredUsers)
|
||||
makeSurePresenterIsAssigned(user)
|
||||
|
||||
log.info("Ejecting user from meeting. meetingId=" + mProps.meetingID + " userId=" + msg.userId)
|
||||
outGW.send(new UserEjectedFromMeeting(mProps.meetingID, mProps.recorded, msg.userId, msg.ejectedBy))
|
||||
outGW.send(new DisconnectUser(mProps.meetingID, msg.userId))
|
||||
log.info("Ejecting user from meeting. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId)
|
||||
outGW.send(new UserEjectedFromMeeting(props.meetingProp.intId, props.recordProp.record, msg.userId, msg.ejectedBy))
|
||||
outGW.send(new DisconnectUser(props.meetingProp.intId, msg.userId))
|
||||
|
||||
outGW.send(new UserLeft(msg.meetingID, mProps.recorded, user))
|
||||
outGW.send(new UserLeft(msg.meetingID, props.recordProp.record, user))
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,8 +269,8 @@ trait UsersApp {
|
||||
for {
|
||||
uvo <- Users.userSharedWebcam(msg.userId, liveMeeting.users, msg.stream)
|
||||
} yield {
|
||||
log.info("User shared webcam. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " stream=" + msg.stream)
|
||||
outGW.send(new UserSharedWebcam(mProps.meetingID, mProps.recorded, uvo.id, msg.stream))
|
||||
log.info("User shared webcam. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " stream=" + msg.stream)
|
||||
outGW.send(new UserSharedWebcam(props.meetingProp.intId, props.recordProp.record, uvo.id, msg.stream))
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,14 +278,14 @@ trait UsersApp {
|
||||
for {
|
||||
uvo <- Users.userUnsharedWebcam(msg.userId, liveMeeting.users, msg.stream)
|
||||
} yield {
|
||||
log.info("User unshared webcam. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " stream=" + msg.stream)
|
||||
outGW.send(new UserUnsharedWebcam(mProps.meetingID, mProps.recorded, uvo.id, msg.stream))
|
||||
log.info("User unshared webcam. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " stream=" + msg.stream)
|
||||
outGW.send(new UserUnsharedWebcam(props.meetingProp.intId, props.recordProp.record, uvo.id, msg.stream))
|
||||
}
|
||||
}
|
||||
|
||||
def handleChangeUserStatus(msg: ChangeUserStatus): Unit = {
|
||||
if (Users.hasUserWithId(msg.userID, liveMeeting.users)) {
|
||||
outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, msg.userID, msg.status, msg.value))
|
||||
outGW.send(new UserStatusChange(props.meetingProp.intId, props.recordProp.record, msg.userID, msg.status, msg.value))
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,14 +294,14 @@ trait UsersApp {
|
||||
}
|
||||
|
||||
def handleUserJoin(msg: UserJoining): Unit = {
|
||||
log.debug("Received user joined meeting. metingId=" + mProps.meetingID + " userId=" + msg.userID)
|
||||
log.debug("Received user joined meeting. metingId=" + props.meetingProp.intId + " userId=" + msg.userID)
|
||||
|
||||
def initVoiceUser(userId: String, ru: RegisteredUser): VoiceUser = {
|
||||
val wUser = Users.findWithId(userId, liveMeeting.users)
|
||||
|
||||
wUser match {
|
||||
case Some(u) => {
|
||||
log.debug("Found user. metingId=" + mProps.meetingID + " userId=" + msg.userID + " user=" + u)
|
||||
log.debug("Found user. metingId=" + props.meetingProp.intId + " userId=" + msg.userID + " user=" + u)
|
||||
if (u.voiceUser.joined) {
|
||||
/*
|
||||
* User is in voice conference. Must mean that the user reconnected with audio
|
||||
@ -312,7 +319,7 @@ trait UsersApp {
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
log.debug("User not found. metingId=" + mProps.meetingID + " userId=" + msg.userID)
|
||||
log.debug("User not found. metingId=" + props.meetingProp.intId + " userId=" + msg.userID)
|
||||
/**
|
||||
* New user. Initialize voice status.
|
||||
*/
|
||||
@ -325,7 +332,7 @@ trait UsersApp {
|
||||
|
||||
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.authToken, msg.userID, liveMeeting.registeredUsers)
|
||||
regUser foreach { ru =>
|
||||
log.debug("Found registered user. metingId=" + mProps.meetingID + " userId=" + msg.userID + " ru=" + ru)
|
||||
log.debug("Found registered user. metingId=" + props.meetingProp.intId + " userId=" + msg.userID + " ru=" + ru)
|
||||
|
||||
val wUser = Users.findWithId(msg.userID, liveMeeting.users)
|
||||
|
||||
@ -333,13 +340,13 @@ trait UsersApp {
|
||||
|
||||
wUser.foreach { w =>
|
||||
if (!w.joinedWeb) {
|
||||
log.debug("User is in voice only. Mark as user left. metingId=" + mProps.meetingID + " userId=" + msg.userID)
|
||||
log.debug("User is in voice only. Mark as user left. metingId=" + props.meetingProp.intId + " userId=" + msg.userID)
|
||||
/**
|
||||
* If user is not joined through the web (perhaps reconnecting).
|
||||
* Send a user left event to clear up user list of all clients.
|
||||
*/
|
||||
val user = Users.userLeft(w.id, liveMeeting.users)
|
||||
outGW.send(new UserLeft(msg.meetingID, mProps.recorded, w))
|
||||
Users.userLeft(w.id, liveMeeting.users)
|
||||
outGW.send(new UserLeft(msg.meetingID, props.recordProp.record, w))
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,29 +354,32 @@ trait UsersApp {
|
||||
* Initialize the newly joined user copying voice status in case this
|
||||
* join is due to a reconnect.
|
||||
*/
|
||||
val waitingForAcceptance = ru.guest && liveMeeting.meetingModel.getGuestPolicy() == GuestPolicy.ASK_MODERATOR && ru.waitingForAcceptance
|
||||
val waitingForAcceptance = ru.guest &&
|
||||
MeetingStatus2x.getGuestPolicy(liveMeeting.status) == GuestPolicy.ASK_MODERATOR && ru.waitingForAcceptance
|
||||
val lockStatus = getInitialLockStatus(ru.role)
|
||||
|
||||
for {
|
||||
uvo <- Users.newUser(msg.userID, lockStatus, ru, waitingForAcceptance, vu, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("User joined meeting. metingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
|
||||
log.info("User joined meeting. metingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
|
||||
|
||||
if (uvo.guest && liveMeeting.meetingModel.getGuestPolicy() == GuestPolicy.ALWAYS_DENY) {
|
||||
outGW.send(new GuestAccessDenied(mProps.meetingID, mProps.recorded, uvo.id))
|
||||
if (uvo.guest && MeetingStatus2x.getGuestPolicy(liveMeeting.status) == GuestPolicy.ALWAYS_DENY) {
|
||||
outGW.send(new GuestAccessDenied(props.meetingProp.intId, props.recordProp.record, uvo.id))
|
||||
} else {
|
||||
outGW.send(new UserJoined(mProps.meetingID, mProps.recorded, uvo))
|
||||
outGW.send(new MeetingState(mProps.meetingID, mProps.recorded, uvo.id, liveMeeting.meetingModel.getPermissions(), liveMeeting.meetingModel.isMeetingMuted()))
|
||||
outGW.send(new UserJoined(props.meetingProp.intId, props.recordProp.record, uvo))
|
||||
outGW.send(new MeetingState(props.meetingProp.intId, props.recordProp.record, uvo.id,
|
||||
MeetingStatus2x.getPermissions(liveMeeting.status), MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
|
||||
|
||||
if (!waitingForAcceptance) {
|
||||
// Become presenter if the only moderator
|
||||
if ((Users.numModerators(liveMeeting.users) == 1) || (Users.hasNoPresenter(liveMeeting.users))) {
|
||||
if (ru.role == Roles.MODERATOR_ROLE) {
|
||||
log.info("Assigning presenter to lone moderator. metingId=" + mProps.meetingID + " userId=" + uvo.id)
|
||||
log.info("Assigning presenter to lone moderator. metingId=" + props.meetingProp.intId + " userId=" + uvo.id)
|
||||
assignNewPresenter(msg.userID, ru.name, msg.userID)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.info("User waiting for acceptance. metingId=" + mProps.meetingID + " userId=" + uvo.id)
|
||||
log.info("User waiting for acceptance. metingId=" + props.meetingProp.intId + " userId=" + uvo.id)
|
||||
}
|
||||
liveMeeting.webUserJoined
|
||||
startRecordingIfAutoStart()
|
||||
@ -382,8 +392,8 @@ trait UsersApp {
|
||||
for {
|
||||
u <- Users.userLeft(msg.userID, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("User left meeting. meetingId=" + mProps.meetingID + " userId=" + u.id + " user=" + u)
|
||||
outGW.send(new UserLeft(msg.meetingID, mProps.recorded, u))
|
||||
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.id + " user=" + u)
|
||||
outGW.send(new UserLeft(msg.meetingID, props.recordProp.record, u))
|
||||
|
||||
makeSurePresenterIsAssigned(u)
|
||||
|
||||
@ -394,7 +404,7 @@ trait UsersApp {
|
||||
* and is reconnecting. Make the user as joined only in the voice conference. If we get a
|
||||
* user left voice conference message, then we will remove the user from the users list.
|
||||
*/
|
||||
switchUserToPhoneUser(new UserJoinedVoiceConfMessage(mProps.voiceBridge,
|
||||
switchUserToPhoneUser(new UserJoinedVoiceConfMessage(props.voiceProp.voiceConf,
|
||||
vu.userId, u.id, u.externalId, vu.callerName,
|
||||
vu.callerNum, vu.muted, vu.talking, vu.avatarURL, u.listenOnly));
|
||||
}
|
||||
@ -406,16 +416,16 @@ trait UsersApp {
|
||||
}
|
||||
|
||||
def getInitialLockStatus(role: String): Boolean = {
|
||||
liveMeeting.meetingModel.getPermissions().lockOnJoin && !role.equals(Roles.MODERATOR_ROLE)
|
||||
MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin && !role.equals(Roles.MODERATOR_ROLE)
|
||||
}
|
||||
|
||||
def handleUserJoinedVoiceFromPhone(msg: UserJoinedVoiceConfMessage) = {
|
||||
log.info("User joining from phone. meetingId=" + mProps.meetingID + " userId=" + msg.userId + " extUserId=" + msg.externUserId)
|
||||
log.info("User joining from phone. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId + " extUserId=" + msg.externUserId)
|
||||
|
||||
val user = Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users) match {
|
||||
Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users) match {
|
||||
case Some(user) => {
|
||||
log.info("Voice user=" + msg.voiceUserId + " is already in conf="
|
||||
+ mProps.voiceBridge + ". Must be duplicate message. meetigId=" + mProps.meetingID)
|
||||
+ props.voiceProp.voiceConf + ". Must be duplicate message. meetigId=" + props.meetingProp.intId)
|
||||
}
|
||||
case None => {
|
||||
val webUserId = if (msg.userId != msg.callerIdName) {
|
||||
@ -441,14 +451,14 @@ trait UsersApp {
|
||||
lockStatus = getInitialLockStatus(Roles.VIEWER_ROLE),
|
||||
listenOnly = msg.listenOnly, avatarURL = msg.avatarURL)
|
||||
|
||||
log.info("User joined from phone. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
|
||||
log.info("User joined from phone. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
|
||||
|
||||
outGW.send(new UserJoined(mProps.meetingID, mProps.recorded, uvo))
|
||||
outGW.send(new UserJoinedVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, uvo))
|
||||
outGW.send(new UserJoined(props.meetingProp.intId, props.recordProp.record, uvo))
|
||||
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, uvo))
|
||||
|
||||
if (liveMeeting.meetingModel.isMeetingMuted()) {
|
||||
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded, uvo.id, uvo.id,
|
||||
mProps.voiceBridge, vu.userId, liveMeeting.meetingModel.isMeetingMuted()))
|
||||
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status)) {
|
||||
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record, uvo.id, uvo.id,
|
||||
props.voiceProp.voiceConf, vu.userId, MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -456,16 +466,17 @@ trait UsersApp {
|
||||
|
||||
def startRecordingVoiceConference() {
|
||||
if (Users.numUsersInVoiceConference(liveMeeting.users) == 1 &&
|
||||
mProps.recorded && !liveMeeting.isVoiceRecording) {
|
||||
liveMeeting.startRecordingVoice()
|
||||
log.info("Send START RECORDING voice conf. meetingId=" + mProps.meetingID + " voice conf=" + mProps.voiceBridge)
|
||||
outGW.send(new StartRecordingVoiceConf(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
props.recordProp.record &&
|
||||
!MeetingStatus2x.isVoiceRecording(liveMeeting.status)) {
|
||||
MeetingStatus2x.startRecordingVoice(liveMeeting.status)
|
||||
log.info("Send START RECORDING voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + props.voiceProp.voiceConf)
|
||||
outGW.send(new StartRecordingVoiceConf(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf))
|
||||
}
|
||||
}
|
||||
|
||||
def switchUserToPhoneUser(msg: UserJoinedVoiceConfMessage) = {
|
||||
log.info("User has been disconnected but still in voice conf. Switching to phone user. meetingId="
|
||||
+ mProps.meetingID + " callername=" + msg.callerIdName
|
||||
+ props.meetingProp.intId + " callername=" + msg.callerIdName
|
||||
+ " userId=" + msg.userId + " extUserId=" + msg.externUserId)
|
||||
|
||||
Users.findWithId(msg.userId, liveMeeting.users) match {
|
||||
@ -473,13 +484,13 @@ trait UsersApp {
|
||||
val nu = Users.switchUserToPhoneUser(user, liveMeeting.users, msg.voiceUserId, msg.userId, msg.callerIdName,
|
||||
msg.callerIdNum, msg.muted, msg.talking, msg.avatarURL, msg.listenOnly)
|
||||
|
||||
log.info("User joined voice. meetingId=" + mProps.meetingID + " userId=" + user.id + " user=" + nu)
|
||||
outGW.send(new UserJoinedVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
|
||||
log.info("User joined voice. meetingId=" + props.meetingProp.intId + " userId=" + user.id + " user=" + nu)
|
||||
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
|
||||
|
||||
if (liveMeeting.meetingModel.isMeetingMuted()) {
|
||||
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded,
|
||||
nu.id, nu.id, mProps.voiceBridge,
|
||||
nu.voiceUser.userId, liveMeeting.meetingModel.isMeetingMuted()))
|
||||
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status)) {
|
||||
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
|
||||
nu.id, nu.id, props.voiceProp.voiceConf,
|
||||
nu.voiceUser.userId, MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
@ -489,7 +500,7 @@ trait UsersApp {
|
||||
}
|
||||
|
||||
def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) = {
|
||||
log.info("Received user joined voice. meetingId=" + mProps.meetingID + " callername=" + msg.callerIdName
|
||||
log.info("Received user joined voice. meetingId=" + props.meetingProp.intId + " callername=" + msg.callerIdName
|
||||
+ " userId=" + msg.userId + " extUserId=" + msg.externUserId)
|
||||
|
||||
Users.findWithId(msg.userId, liveMeeting.users) match {
|
||||
@ -500,12 +511,12 @@ trait UsersApp {
|
||||
val nu = Users.restoreMuteState(user, liveMeeting.users, msg.voiceUserId, msg.userId, msg.callerIdName,
|
||||
msg.callerIdNum, msg.muted, msg.talking, msg.avatarURL, msg.listenOnly)
|
||||
|
||||
log.info("User joined voice. meetingId=" + mProps.meetingID + " userId=" + user.id + " user=" + nu)
|
||||
outGW.send(new UserJoinedVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
|
||||
log.info("User joined voice. meetingId=" + props.meetingProp.intId + " userId=" + user.id + " user=" + nu)
|
||||
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
|
||||
|
||||
if (liveMeeting.meetingModel.isMeetingMuted() || previouslyMuted) {
|
||||
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded,
|
||||
nu.id, nu.id, mProps.voiceBridge,
|
||||
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status) || previouslyMuted) {
|
||||
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
|
||||
nu.id, nu.id, props.voiceProp.voiceConf,
|
||||
nu.voiceUser.userId, true))
|
||||
}
|
||||
|
||||
@ -520,30 +531,31 @@ trait UsersApp {
|
||||
|
||||
def stopRecordingVoiceConference() {
|
||||
if (Users.numUsersInVoiceConference(liveMeeting.users) == 0 &&
|
||||
mProps.recorded && liveMeeting.isVoiceRecording) {
|
||||
liveMeeting.stopRecordingVoice()
|
||||
log.info("Send STOP RECORDING voice conf. meetingId=" + mProps.meetingID + " voice conf=" + mProps.voiceBridge)
|
||||
outGW.send(new StopRecordingVoiceConf(mProps.meetingID, mProps.recorded,
|
||||
mProps.voiceBridge, liveMeeting.meetingModel.getVoiceRecordingFilename()))
|
||||
props.recordProp.record &&
|
||||
MeetingStatus2x.isVoiceRecording(liveMeeting.status)) {
|
||||
MeetingStatus2x.stopRecordingVoice(liveMeeting.status)
|
||||
log.info("Send STOP RECORDING voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + props.voiceProp.voiceConf)
|
||||
outGW.send(new StopRecordingVoiceConf(props.meetingProp.intId, props.recordProp.record,
|
||||
props.voiceProp.voiceConf, MeetingStatus2x.getVoiceRecordingFilename(liveMeeting.status)))
|
||||
}
|
||||
}
|
||||
|
||||
def handleUserLeftVoiceConfMessage(msg: UserLeftVoiceConfMessage) {
|
||||
log.info("Received user left voice conf. meetingId=" + mProps.meetingID + " voice conf=" + msg.voiceConfId
|
||||
log.info("Received user left voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + msg.voiceConfId
|
||||
+ " userId=" + msg.voiceUserId)
|
||||
|
||||
for {
|
||||
user <- Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users)
|
||||
nu = Users.resetVoiceUser(user, liveMeeting.users)
|
||||
} yield {
|
||||
log.info("User left voice conf. meetingId=" + mProps.meetingID + " userId=" + nu.id + " user=" + nu)
|
||||
outGW.send(new UserLeftVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
|
||||
log.info("User left voice conf. meetingId=" + props.meetingProp.intId + " userId=" + nu.id + " user=" + nu)
|
||||
outGW.send(new UserLeftVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
|
||||
|
||||
if (user.phoneUser) {
|
||||
for {
|
||||
userLeaving <- Users.userLeft(user.id, liveMeeting.users)
|
||||
} yield {
|
||||
outGW.send(new UserLeft(mProps.meetingID, mProps.recorded, userLeaving))
|
||||
outGW.send(new UserLeft(props.meetingProp.intId, props.recordProp.record, userLeaving))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -557,9 +569,9 @@ trait UsersApp {
|
||||
user <- Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users)
|
||||
nu = Users.setUserMuted(user, liveMeeting.users, msg.muted)
|
||||
} yield {
|
||||
log.info("User muted in voice conf. meetingId=" + mProps.meetingID + " userId=" + nu.id + " user=" + nu)
|
||||
log.info("User muted in voice conf. meetingId=" + props.meetingProp.intId + " userId=" + nu.id + " user=" + nu)
|
||||
|
||||
outGW.send(new UserVoiceMuted(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
|
||||
outGW.send(new UserVoiceMuted(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,7 +580,7 @@ trait UsersApp {
|
||||
user <- Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users)
|
||||
nv = Users.setUserTalking(user, liveMeeting.users, msg.talking)
|
||||
} yield {
|
||||
outGW.send(new UserVoiceTalking(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nv))
|
||||
outGW.send(new UserVoiceTalking(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nv))
|
||||
}
|
||||
}
|
||||
|
||||
@ -578,14 +590,14 @@ trait UsersApp {
|
||||
|
||||
def assignNewPresenter(newPresenterID: String, newPresenterName: String, assignedBy: String) {
|
||||
// Stop poll if one is running as presenter left.
|
||||
handleStopPollRequest(StopPollRequest(mProps.meetingID, assignedBy))
|
||||
handleStopPollRequest(StopPollRequest(props.meetingProp.intId, assignedBy))
|
||||
|
||||
def removePresenterRightsToCurrentPresenter(): Unit = {
|
||||
for {
|
||||
curPres <- Users.getCurrentPresenter(liveMeeting.users)
|
||||
} yield {
|
||||
Users.unbecomePresenter(curPres.id, liveMeeting.users)
|
||||
outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, curPres.id, "presenter", false: java.lang.Boolean))
|
||||
outGW.send(new UserStatusChange(props.meetingProp.intId, props.recordProp.record, curPres.id, "presenter", false: java.lang.Boolean))
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,9 +606,9 @@ trait UsersApp {
|
||||
} yield {
|
||||
removePresenterRightsToCurrentPresenter()
|
||||
Users.becomePresenter(newPres.id, liveMeeting.users)
|
||||
liveMeeting.setCurrentPresenterInfo(new Presenter(newPresenterID, newPresenterName, assignedBy))
|
||||
outGW.send(new PresenterAssigned(mProps.meetingID, mProps.recorded, new Presenter(newPresenterID, newPresenterName, assignedBy)))
|
||||
outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, newPresenterID, "presenter", true: java.lang.Boolean))
|
||||
MeetingStatus2x.setCurrentPresenterInfo(liveMeeting.status, new Presenter(newPresenterID, newPresenterName, assignedBy))
|
||||
outGW.send(new PresenterAssigned(props.meetingProp.intId, props.recordProp.record, new Presenter(newPresenterID, newPresenterName, assignedBy)))
|
||||
outGW.send(new UserStatusChange(props.meetingProp.intId, props.recordProp.record, newPresenterID, "presenter", true: java.lang.Boolean))
|
||||
}
|
||||
}
|
||||
|
||||
@ -613,9 +625,9 @@ trait UsersApp {
|
||||
if (msg.response == true) {
|
||||
val nu = Users.setWaitingForAcceptance(user, liveMeeting.users, false)
|
||||
RegisteredUsers.updateRegUser(nu, liveMeeting.registeredUsers)
|
||||
outGW.send(new UserJoined(mProps.meetingID, mProps.recorded, nu))
|
||||
outGW.send(new UserJoined(props.meetingProp.intId, props.recordProp.record, nu))
|
||||
} else {
|
||||
outGW.send(new GuestAccessDenied(mProps.meetingID, mProps.recorded, user.id))
|
||||
outGW.send(new GuestAccessDenied(props.meetingProp.intId, props.recordProp.record, user.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,73 +0,0 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.util.RandomStringGenerator
|
||||
import org.bigbluebutton.core.api.Presenter
|
||||
import org.bigbluebutton.core.models._
|
||||
|
||||
class UsersModel {
|
||||
// private var uservos = new Users
|
||||
|
||||
// private var regUsers = new RegisteredUsers
|
||||
|
||||
/* When reconnecting SIP global audio, users may receive the connection message
|
||||
* before the disconnection message.
|
||||
* This variable is a connection counter that should control this scenario.
|
||||
*/
|
||||
private var globalAudioConnectionCounter = new collection.immutable.HashMap[String, Integer]
|
||||
|
||||
private var locked = false
|
||||
private var meetingMuted = false
|
||||
private var recordingVoice = false
|
||||
|
||||
private var currentPresenter = new Presenter("system", "system", "system")
|
||||
|
||||
def setCurrentPresenterInfo(pres: Presenter) {
|
||||
currentPresenter = pres
|
||||
}
|
||||
|
||||
def getCurrentPresenterInfo(): Presenter = {
|
||||
currentPresenter
|
||||
}
|
||||
|
||||
def addGlobalAudioConnection(userID: String): Boolean = {
|
||||
globalAudioConnectionCounter.get(userID) match {
|
||||
case Some(vc) => {
|
||||
globalAudioConnectionCounter += userID -> (vc + 1)
|
||||
false
|
||||
}
|
||||
case None => {
|
||||
globalAudioConnectionCounter += userID -> 1
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def removeGlobalAudioConnection(userID: String): Boolean = {
|
||||
globalAudioConnectionCounter.get(userID) match {
|
||||
case Some(vc) => {
|
||||
if (vc == 1) {
|
||||
globalAudioConnectionCounter -= userID
|
||||
true
|
||||
} else {
|
||||
globalAudioConnectionCounter += userID -> (vc - 1)
|
||||
false
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingVoice() {
|
||||
recordingVoice = true
|
||||
}
|
||||
|
||||
def stopRecordingVoice() {
|
||||
recordingVoice = false
|
||||
}
|
||||
|
||||
def isVoiceRecording: Boolean = {
|
||||
recordingVoice
|
||||
}
|
||||
}
|
@ -42,44 +42,44 @@ trait WhiteboardApp {
|
||||
}
|
||||
if (liveMeeting.wbModel.hasWhiteboard(wbId)) {
|
||||
// println("WhiteboardApp::handleSendWhiteboardAnnotationRequest - num shapes [" + wb.shapes.length + "]")
|
||||
outGW.send(new SendWhiteboardAnnotationEvent(mProps.meetingID, mProps.recorded, msg.requesterID, wbId, shape))
|
||||
outGW.send(new SendWhiteboardAnnotationEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, wbId, shape))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def handleSendCursorPositionRequest(msg: SendCursorPositionRequest) {
|
||||
outGW.send(new CursorPositionUpdatedEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.xPercent, msg.yPercent))
|
||||
outGW.send(new CursorPositionUpdatedEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.xPercent, msg.yPercent))
|
||||
}
|
||||
|
||||
def handleGetWhiteboardShapesRequest(msg: GetWhiteboardShapesRequest) {
|
||||
//println("WB: Received page history [" + msg.whiteboardId + "]")
|
||||
val history = liveMeeting.wbModel.getHistory(msg.whiteboardId);
|
||||
if (history.length > 0) {
|
||||
outGW.send(new GetWhiteboardShapesReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.whiteboardId, history, msg.replyTo))
|
||||
outGW.send(new GetWhiteboardShapesReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.whiteboardId, history, msg.replyTo))
|
||||
}
|
||||
}
|
||||
|
||||
def handleClearWhiteboardRequest(msg: ClearWhiteboardRequest) {
|
||||
//println("WB: Received clear whiteboard")
|
||||
liveMeeting.wbModel.clearWhiteboard(msg.whiteboardId, msg.requesterID) foreach { fullClear =>
|
||||
outGW.send(new ClearWhiteboardEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.whiteboardId, fullClear))
|
||||
outGW.send(new ClearWhiteboardEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.whiteboardId, fullClear))
|
||||
}
|
||||
}
|
||||
|
||||
def handleUndoWhiteboardRequest(msg: UndoWhiteboardRequest) {
|
||||
// println("WB: Received undo whiteboard")
|
||||
liveMeeting.wbModel.undoWhiteboard(msg.whiteboardId, msg.requesterID) foreach { last =>
|
||||
outGW.send(new UndoWhiteboardEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.whiteboardId, last.id))
|
||||
outGW.send(new UndoWhiteboardEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.whiteboardId, last.id))
|
||||
}
|
||||
}
|
||||
|
||||
def handleModifyWhiteboardAccessRequest(msg: ModifyWhiteboardAccessRequest) {
|
||||
liveMeeting.wbModel.modifyWhiteboardAccess(msg.multiUser)
|
||||
outGW.send(new ModifiedWhiteboardAccessEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.multiUser))
|
||||
outGW.send(new ModifiedWhiteboardAccessEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.multiUser))
|
||||
}
|
||||
|
||||
def handleGetWhiteboardAccessRequest(msg: GetWhiteboardAccessRequest) {
|
||||
val multiUser = liveMeeting.wbModel.getWhiteboardAccess()
|
||||
outGW.send(new GetWhiteboardAccessReply(mProps.meetingID, mProps.recorded, msg.requesterID, multiUser))
|
||||
outGW.send(new GetWhiteboardAccessReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, multiUser))
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.bigbluebutton.core.util.jhotdraw.BezierWrapper
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.collection.immutable.HashMap
|
||||
|
||||
@ -208,4 +206,4 @@ class WhiteboardModel {
|
||||
def getWhiteboardAccess(): Boolean = {
|
||||
_multiUser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
package org.bigbluebutton.core.bus
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.event.{ EventBus, LookupClassification }
|
||||
import org.bigbluebutton.common2.messages.BbbCommonEnvCoreMsg
|
||||
|
||||
case class BbbMsgEvent(val topic: String, val payload: BbbCommonEnvCoreMsg)
|
||||
|
||||
class BbbMsgRouterEventBus extends EventBus with LookupClassification {
|
||||
type Event = BbbMsgEvent
|
||||
type Classifier = String
|
||||
type Subscriber = ActorRef
|
||||
|
||||
// is used for extracting the classifier from the incoming events
|
||||
override protected def classify(event: Event): Classifier = event.topic
|
||||
|
||||
// will be invoked for each event for all subscribers which registered themselves
|
||||
// for the event’s classifier
|
||||
override protected def publish(event: Event, subscriber: Subscriber): Unit = {
|
||||
subscriber ! event.payload
|
||||
}
|
||||
|
||||
// must define a full order over the subscribers, expressed as expected from
|
||||
// `java.lang.Comparable.compare`
|
||||
override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
|
||||
a.compareTo(b)
|
||||
|
||||
// determines the initial size of the index data structure
|
||||
// used internally (i.e. the expected number of different classifiers)
|
||||
override protected def mapSize: Int = 128
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.bus
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.event.EventBus
|
||||
import akka.event.ActorEventBus
|
||||
import akka.event.LookupClassification
|
||||
import org.bigbluebutton.core.api.InMessage
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
package org.bigbluebutton.core.bus
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.event.{ EventBus, LookupClassification }
|
||||
|
||||
case class ReceivedJsonMessage(channel: String, data: String)
|
||||
case class IncomingJsonMessage(val topic: String, val payload: ReceivedJsonMessage)
|
||||
|
||||
class IncomingJsonMessageBus extends EventBus with LookupClassification {
|
||||
type Event = IncomingJsonMessage
|
||||
type Classifier = String
|
||||
type Subscriber = ActorRef
|
||||
|
||||
// is used for extracting the classifier from the incoming events
|
||||
override protected def classify(event: Event): Classifier = event.topic
|
||||
|
||||
// will be invoked for each event for all subscribers which registered themselves
|
||||
// for the event’s classifier
|
||||
override protected def publish(event: Event, subscriber: Subscriber): Unit = {
|
||||
subscriber ! event.payload
|
||||
}
|
||||
|
||||
// must define a full order over the subscribers, expressed as expected from
|
||||
// `java.lang.Comparable.compare`
|
||||
override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
|
||||
a.compareTo(b)
|
||||
|
||||
// determines the initial size of the index data structure
|
||||
// used internally (i.e. the expected number of different classifiers)
|
||||
override protected def mapSize: Int = 128
|
||||
}
|
31
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/bus/OutEventBus2.scala
Executable file
31
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/bus/OutEventBus2.scala
Executable file
@ -0,0 +1,31 @@
|
||||
package org.bigbluebutton.core.bus
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.event.{ EventBus, LookupClassification }
|
||||
import org.bigbluebutton.common2.messages.{ BbbCommonEnvCoreMsg }
|
||||
|
||||
case class BbbOutMessage(val topic: String, val payload: BbbCommonEnvCoreMsg)
|
||||
|
||||
class OutEventBus2 extends EventBus with LookupClassification {
|
||||
type Event = BbbOutMessage
|
||||
type Classifier = String
|
||||
type Subscriber = ActorRef
|
||||
|
||||
// is used for extracting the classifier from the incoming events
|
||||
override protected def classify(event: Event): Classifier = event.topic
|
||||
|
||||
// will be invoked for each event for all subscribers which registered themselves
|
||||
// for the event’s classifier
|
||||
override protected def publish(event: Event, subscriber: Subscriber): Unit = {
|
||||
subscriber ! event.payload
|
||||
}
|
||||
|
||||
// must define a full order over the subscribers, expressed as expected from
|
||||
// `java.lang.Comparable.compare`
|
||||
override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
|
||||
a.compareTo(b)
|
||||
|
||||
// determines the initial size of the index data structure
|
||||
// used internally (i.e. the expected number of different classifiers)
|
||||
override protected def mapSize: Int = 128
|
||||
}
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.bus
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.event.EventBus
|
||||
import akka.event.ActorEventBus
|
||||
import akka.event.LookupClassification
|
||||
import org.bigbluebutton.core.api._
|
||||
|
||||
|
125
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingStatus.scala
Executable file
125
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingStatus.scala
Executable file
@ -0,0 +1,125 @@
|
||||
package org.bigbluebutton.core.domain
|
||||
|
||||
import com.softwaremill.quicklens._
|
||||
import org.bigbluebutton.core.api.Permissions
|
||||
|
||||
case class MeetingProperties(
|
||||
id: String,
|
||||
extId: String,
|
||||
name: String,
|
||||
recorded: Boolean,
|
||||
voiceConf: String,
|
||||
duration: Int,
|
||||
autoStartRecording: Boolean,
|
||||
allowStartStopRecording: Boolean,
|
||||
moderatorPass: String,
|
||||
viewerPass: String,
|
||||
createTime: Long,
|
||||
createDate: String,
|
||||
isBreakout: Boolean)
|
||||
|
||||
case class MeetingProperties2x(
|
||||
id: String,
|
||||
extId: String,
|
||||
name: String,
|
||||
voiceConf: String,
|
||||
duration: Int,
|
||||
maxUsers: Int,
|
||||
allowVoiceOnly: Boolean,
|
||||
isBreakout: Boolean,
|
||||
extensionProp: MeetingExtensionProp,
|
||||
recordingProp: MeetingRecordingProp)
|
||||
|
||||
case class MeetingRecordingProp(
|
||||
recorded: Boolean = false,
|
||||
autoStartRecording: Boolean = false,
|
||||
allowStartStopRecording: Boolean = true)
|
||||
|
||||
case class MeetingExtensionProp(
|
||||
maxExtensions: Int = 0,
|
||||
extendByMinutes: Int = 20,
|
||||
sendNotice: Boolean = true)
|
||||
|
||||
case class MeetingRecordingStatus(
|
||||
recording: Boolean = false,
|
||||
voiceRecordingFilename: String = "")
|
||||
|
||||
case class MeetingExtensionStatus(
|
||||
numExtensions: Int = 0,
|
||||
sent15MinNotice: Boolean = false,
|
||||
sent10MinNotice: Boolean = false,
|
||||
sent5MinNotice: Boolean = false)
|
||||
|
||||
case class Meeting3x(permissions: Permissions,
|
||||
isRecording: Boolean = false,
|
||||
muted: Boolean = false,
|
||||
ended: Boolean = false,
|
||||
hasLastWebUserLeft: Boolean = false,
|
||||
lastWebUserLeftOnTimestamp: Long = 0L,
|
||||
voiceRecordingFilename: String = "",
|
||||
startedOn: Long = 0L,
|
||||
pinNumbers: Set[String] = Set.empty,
|
||||
lastGeneratedPin: Int = 0,
|
||||
breakoutRoomsStartedOn: Long = 0L,
|
||||
breakoutRoomsDurationInMinutes: Int = 120,
|
||||
extensionStatus: MeetingExtensionStatus = new MeetingExtensionStatus,
|
||||
recordingStatus: MeetingRecordingStatus = new MeetingRecordingStatus)
|
||||
|
||||
object Meeting3x {
|
||||
def isExtensionAllowed(
|
||||
extension: MeetingExtensionProp,
|
||||
status: MeetingExtensionStatus): Boolean = status.numExtensions < extension.maxExtensions
|
||||
|
||||
def incNumExtension(extension: MeetingExtensionProp, status: MeetingExtensionStatus): MeetingExtensionStatus = {
|
||||
if (status.numExtensions < extension.maxExtensions) {
|
||||
modify(status)(_.numExtensions).setTo(status.numExtensions + 1)
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
def fifteenMinutesNoticeSent(extension: MeetingExtensionStatus): MeetingExtensionStatus = {
|
||||
modify(extension)(_.sent15MinNotice).setTo(true)
|
||||
}
|
||||
|
||||
def tenMinutesNoticeSent(extension: MeetingExtensionStatus): MeetingExtensionStatus = {
|
||||
modify(extension)(_.sent10MinNotice).setTo(true)
|
||||
}
|
||||
|
||||
def fiveMinutesNoticeSent(extension: MeetingExtensionStatus): MeetingExtensionStatus = {
|
||||
modify(extension)(_.sent5MinNotice).setTo(true)
|
||||
}
|
||||
|
||||
def mute(meeting: Meeting3x): Meeting3x = {
|
||||
modify(meeting)(_.muted).setTo(true)
|
||||
}
|
||||
|
||||
def unMute(meeting: Meeting3x): Meeting3x = {
|
||||
modify(meeting)(_.muted).setTo(false)
|
||||
}
|
||||
|
||||
def recordingStarted(meeting: Meeting3x): Meeting3x = {
|
||||
modify(meeting)(_.isRecording).setTo(true)
|
||||
}
|
||||
|
||||
def recordingStopped(meeting: Meeting3x): Meeting3x = {
|
||||
modify(meeting)(_.isRecording).setTo(false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object PinNumberGenerator {
|
||||
def generatePin(conf: String, meeting: Meeting3x): String = {
|
||||
def inc(curPin: Int): Int = {
|
||||
if ((curPin + 1) < 1000) curPin + 1
|
||||
else 1
|
||||
}
|
||||
|
||||
def genAvailablePin(): Int = {
|
||||
val pin = conf.toInt + inc(meeting.lastGeneratedPin)
|
||||
if (meeting.pinNumbers.contains(pin.toString)) genAvailablePin
|
||||
pin
|
||||
}
|
||||
|
||||
genAvailablePin.toString
|
||||
}
|
||||
}
|
@ -37,10 +37,13 @@ class PublicChats {
|
||||
}
|
||||
|
||||
object PublicChat {
|
||||
|
||||
def append(chat: PublicChat, msg: PublicChatMessage): PublicChatMessage = {
|
||||
chat.append(msg)
|
||||
}
|
||||
|
||||
def getMessages(chat: PublicChat): Vector[PublicChatMessage] = {
|
||||
chat.toVector
|
||||
}
|
||||
}
|
||||
|
||||
class PublicChat(val id: String) {
|
||||
|
@ -254,33 +254,12 @@ class Users {
|
||||
|
||||
case class UserIdAndName(id: String, name: String)
|
||||
|
||||
case class UserVO(
|
||||
id: String,
|
||||
externalId: String,
|
||||
name: String,
|
||||
role: String,
|
||||
guest: Boolean,
|
||||
authed: Boolean,
|
||||
waitingForAcceptance: Boolean,
|
||||
emojiStatus: String,
|
||||
presenter: Boolean,
|
||||
hasStream: Boolean,
|
||||
locked: Boolean,
|
||||
webcamStreams: Set[String],
|
||||
phoneUser: Boolean,
|
||||
voiceUser: VoiceUser,
|
||||
listenOnly: Boolean,
|
||||
avatarURL: String,
|
||||
case class UserVO(id: String, externalId: String, name: String, role: String,
|
||||
guest: Boolean, authed: Boolean, waitingForAcceptance: Boolean, emojiStatus: String,
|
||||
presenter: Boolean, hasStream: Boolean, locked: Boolean, webcamStreams: Set[String],
|
||||
phoneUser: Boolean, voiceUser: VoiceUser, listenOnly: Boolean, avatarURL: String,
|
||||
joinedWeb: Boolean)
|
||||
|
||||
case class VoiceUser(
|
||||
userId: String,
|
||||
webUserId: String,
|
||||
callerName: String,
|
||||
callerNum: String,
|
||||
joined: Boolean,
|
||||
locked: Boolean,
|
||||
muted: Boolean,
|
||||
talking: Boolean,
|
||||
avatarURL: String,
|
||||
listenOnly: Boolean)
|
||||
case class VoiceUser(userId: String, webUserId: String, callerName: String,
|
||||
callerNum: String, joined: Boolean, locked: Boolean, muted: Boolean,
|
||||
talking: Boolean, avatarURL: String, listenOnly: Boolean)
|
||||
|
31
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala
Executable file
31
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala
Executable file
@ -0,0 +1,31 @@
|
||||
package org.bigbluebutton.core.models
|
||||
|
||||
object Webcams {
|
||||
def findWithStreamId(webcams: Webcams, streamId: String): Option[WebcamStream] = {
|
||||
webcams.toVector.find(w => w.stream.id == streamId)
|
||||
}
|
||||
|
||||
def findWebcamsForUser(webcams: Webcams, userId: String): Vector[WebcamStream] = {
|
||||
webcams.toVector.filter(w => w.userId == userId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Webcams {
|
||||
private var webcams: collection.immutable.HashMap[String, WebcamStream] = new collection.immutable.HashMap[String, WebcamStream]
|
||||
|
||||
private def toVector: Vector[WebcamStream] = webcams.values.toVector
|
||||
|
||||
private def save(webcam: WebcamStream): WebcamStream = {
|
||||
webcams += webcam.stream.id -> webcam
|
||||
webcam
|
||||
}
|
||||
|
||||
private def remove(streamId: String): Option[WebcamStream] = {
|
||||
val webcam = webcams.get(streamId)
|
||||
webcam foreach (u => webcams -= streamId)
|
||||
webcam
|
||||
}
|
||||
}
|
||||
|
||||
case class WebcamStream(userId: String, stream: Stream)
|
@ -0,0 +1,19 @@
|
||||
package org.bigbluebutton.core.pubsub
|
||||
|
||||
import akka.actor.{ Actor, ActorLogging, Props }
|
||||
import org.bigbluebutton.core.MessageSender
|
||||
//import org.bigbluebutton.common2.messages.MeetingCreatedEvt
|
||||
|
||||
object JsonMsgSenderActor {
|
||||
def props(msgSender: MessageSender): Props =
|
||||
Props(classOf[JsonMsgSenderActor], msgSender)
|
||||
}
|
||||
|
||||
class JsonMsgSenderActor(val service: MessageSender)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
def receive = {
|
||||
// case msg: MeetingCreatedEvt => println(msg)
|
||||
case msg: String => println(msg)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.bigbluebutton.core.pubsub.handlers
|
||||
|
||||
class BbbMsgHdlr {
|
||||
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import org.bigbluebutton.common.messages.Constants
|
||||
import scala.collection.mutable.HashMap
|
||||
import org.bigbluebutton.core.api._
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
|
||||
|
@ -1,13 +1,9 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import scala.collection.mutable.HashMap
|
||||
import org.bigbluebutton.core.api._
|
||||
import com.google.gson.Gson
|
||||
import scala.collection.mutable.HashMap
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import java.util.ArrayList
|
||||
import org.bigbluebutton.common.messages.MessagingConstants
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.common.messages.ChatKeyUtil
|
||||
|
||||
|
@ -4,10 +4,6 @@ import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.messages._
|
||||
|
||||
import spray.json.JsArray
|
||||
import spray.json.JsObject
|
||||
import spray.json.JsString
|
||||
|
||||
object MeetingMessageToJsonConverter {
|
||||
def meetingDestroyedToJson(msg: MeetingDestroyed): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.pubsub.senders
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.core.apps.Page
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
object PesentationMessageToJsonConverter {
|
||||
|
@ -0,0 +1,44 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common2.messages._
|
||||
import org.bigbluebutton.core.bus.{ BbbMsgEvent, BbbMsgRouterEventBus, ReceivedJsonMessage }
|
||||
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
trait ReceivedJsonMsgDeserializer extends SystemConfiguration {
|
||||
this: ReceivedJsonMsgHandlerActor =>
|
||||
|
||||
object JsonDeserializer extends Deserializer
|
||||
|
||||
def deserializeCreateMeetingReqMsg(jsonNode: JsonNode): Option[CreateMeetingReqMsg] = {
|
||||
JsonDeserializer.toBbbCommonMsg[CreateMeetingReqMsg](jsonNode) match {
|
||||
case Success(msg) => Some(msg.asInstanceOf[CreateMeetingReqMsg])
|
||||
case Failure(ex) =>
|
||||
log.error("Failed to CreateMeetingReqMsg message " + ex)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def routeValidateAuthTokenReqMsg(jsonNode: JsonNode): Option[ValidateAuthTokenReqMsg] = {
|
||||
JsonDeserializer.toBbbCommonMsg[ValidateAuthTokenReqMsg](jsonNode) match {
|
||||
case Success(msg) => Some(msg.asInstanceOf[ValidateAuthTokenReqMsg])
|
||||
case Failure(ex) =>
|
||||
log.error("Failed to ValidateAuthTokenReqMsg message " + ex)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def routeRegisterUserReqMsg(jsonNode: JsonNode): Option[RegisterUserReqMsg] = {
|
||||
JsonDeserializer.toBbbCommonMsg[RegisterUserReqMsg](jsonNode) match {
|
||||
case Success(msg) =>
|
||||
// Route via meeting manager as there is a race condition if we send directly to meeting
|
||||
// because the meeting actor might not have been created yet.
|
||||
Some(msg.asInstanceOf[RegisterUserReqMsg])
|
||||
case Failure(ex) =>
|
||||
log.error("Failed to RegisterUserReqMsg message " + ex)
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import akka.actor.{ Actor, ActorLogging, Props }
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import org.bigbluebutton.common2.messages.{ BbbCoreEnvelope, CreateMeetingReqMsg, RegisterUserReqMsg, ValidateAuthTokenReqMsg }
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core2.ReceivedMessageRouter
|
||||
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
object ReceivedJsonMsgHandlerActor {
|
||||
def props(eventBus: BbbMsgRouterEventBus, incomingJsonMessageBus: IncomingJsonMessageBus): Props =
|
||||
Props(classOf[ReceivedJsonMsgHandlerActor], eventBus, incomingJsonMessageBus)
|
||||
}
|
||||
|
||||
class ReceivedJsonMsgHandlerActor(
|
||||
val eventBus: BbbMsgRouterEventBus,
|
||||
val incomingJsonMessageBus: IncomingJsonMessageBus)
|
||||
extends Actor with ActorLogging
|
||||
with SystemConfiguration
|
||||
with ReceivedJsonMsgDeserializer
|
||||
with ReceivedMessageRouter {
|
||||
|
||||
def receive = {
|
||||
case msg: ReceivedJsonMessage =>
|
||||
log.debug("handling {} - {}", msg.channel, msg.data)
|
||||
handleReceivedJsonMessage(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
def handleReceivedJsonMessage(msg: ReceivedJsonMessage): Unit = {
|
||||
for {
|
||||
envJsonNode <- JsonDeserializer.toBbbCommonEnvJsNodeMsg(msg.data)
|
||||
} yield route(envJsonNode.envelope, envJsonNode.core)
|
||||
}
|
||||
|
||||
def route(envelope: BbbCoreEnvelope, jsonNode: JsonNode): Unit = {
|
||||
log.debug("*************** Route envelope name " + envelope.name)
|
||||
envelope.name match {
|
||||
case CreateMeetingReqMsg.NAME =>
|
||||
log.debug("**************** Route CreateMeetingReqMsg")
|
||||
for {
|
||||
m <- deserializeCreateMeetingReqMsg(jsonNode)
|
||||
} yield {
|
||||
log.debug("************ Sending CreateMeetingReqMsg")
|
||||
send(envelope, m)
|
||||
}
|
||||
case ValidateAuthTokenReqMsg.NAME =>
|
||||
log.debug("**************** Route ValidateAuthTokenReqMsg")
|
||||
for {
|
||||
m <- routeValidateAuthTokenReqMsg(jsonNode)
|
||||
} yield {
|
||||
log.debug("************ Sending ValidateAuthTokenReqMsg")
|
||||
send(envelope, m)
|
||||
}
|
||||
case RegisterUserReqMsg.NAME =>
|
||||
log.debug("**************** Route RegisterUserReqMsg")
|
||||
for {
|
||||
m <- routeRegisterUserReqMsg(jsonNode)
|
||||
} yield {
|
||||
log.debug("************ Sending RegisterUserReqMsg")
|
||||
send(envelope, m)
|
||||
}
|
||||
case _ =>
|
||||
log.debug("************ Cannot route envelope name " + envelope.name)
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
1
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/SharedNotesMessageToJsonConverter.scala
Normal file → Executable file
1
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/SharedNotesMessageToJsonConverter.scala
Normal file → Executable file
@ -2,7 +2,6 @@ package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.core.api._
|
||||
import com.google.gson.Gson
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
object SharedNotesMessageToJsonConverter {
|
||||
|
@ -1,13 +1,9 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.common.messages.MessagingConstants
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import com.google.gson.Gson
|
||||
import org.bigbluebutton.core.models.{ RegisteredUser, UserVO }
|
||||
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
object UsersMessageToJsonConverter {
|
||||
private def userToMap(user: UserVO): java.util.Map[String, Any] = {
|
||||
@ -38,9 +34,9 @@ object UsersMessageToJsonConverter {
|
||||
vuser += "muted" -> user.voiceUser.muted
|
||||
vuser += "talking" -> user.voiceUser.talking
|
||||
|
||||
wuser.put("voiceUser", mapAsJavaMap(vuser))
|
||||
wuser.put("voiceUser", mapAsJavaMapConverter(vuser).asJava)
|
||||
|
||||
mapAsJavaMap(wuser)
|
||||
mapAsJavaMapConverter(wuser).asJava
|
||||
}
|
||||
|
||||
private def registeredUserToMap(user: RegisteredUser): java.util.Map[String, Any] = {
|
||||
@ -54,7 +50,7 @@ object UsersMessageToJsonConverter {
|
||||
wuser += "guest" -> user.guest
|
||||
wuser += "waiting_for_acceptance" -> user.waitingForAcceptance
|
||||
|
||||
mapAsJavaMap(wuser)
|
||||
mapAsJavaMapConverter(wuser).asJava
|
||||
}
|
||||
|
||||
private def buildPermissionsHashMap(perms: Permissions): java.util.HashMap[String, java.lang.Boolean] = {
|
||||
|
@ -3,8 +3,7 @@ package org.bigbluebutton.core.pubsub.senders
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.core.apps.AnnotationVO
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
object WhiteboardMessageToJsonConverter {
|
||||
private def shapeToMap(shape: AnnotationVO): java.util.Map[String, Any] = {
|
||||
@ -20,9 +19,9 @@ object WhiteboardMessageToJsonConverter {
|
||||
for ((key, value) <- shape.shape) {
|
||||
shapeMap += key -> value
|
||||
}
|
||||
res += "shape" -> mapAsJavaMap(shapeMap)
|
||||
res += "shape" -> mapAsJavaMapConverter(shapeMap).asJava
|
||||
|
||||
mapAsJavaMap(res)
|
||||
mapAsJavaMapConverter(res).asJava
|
||||
}
|
||||
|
||||
def getWhiteboardShapesReplyToJson(msg: GetWhiteboardShapesReply): String = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,78 +2,46 @@ package org.bigbluebutton.core.running
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
|
||||
import org.bigbluebutton.core.{ MeetingModel, MeetingProperties }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
class LiveMeeting(val mProps: MeetingProperties,
|
||||
val chatModel: ChatModel,
|
||||
val layoutModel: LayoutModel,
|
||||
val meetingModel: MeetingModel,
|
||||
private val usersModel: UsersModel,
|
||||
val users: Users,
|
||||
val registeredUsers: RegisteredUsers,
|
||||
val pollModel: PollModel,
|
||||
val wbModel: WhiteboardModel,
|
||||
val presModel: PresentationModel,
|
||||
val breakoutModel: BreakoutRoomModel,
|
||||
val captionModel: CaptionModel,
|
||||
val notesModel: SharedNotesModel)(implicit val context: ActorContext) {
|
||||
|
||||
val log = Logging(context.system, getClass)
|
||||
class LiveMeeting(val props: DefaultProps,
|
||||
val status: MeetingStatus2x,
|
||||
val chatModel: ChatModel,
|
||||
val layoutModel: LayoutModel,
|
||||
val users: Users,
|
||||
val registeredUsers: RegisteredUsers,
|
||||
val pollModel: PollModel,
|
||||
val wbModel: WhiteboardModel,
|
||||
val presModel: PresentationModel,
|
||||
val breakoutModel: BreakoutRoomModel,
|
||||
val captionModel: CaptionModel,
|
||||
val notesModel: SharedNotesModel)
|
||||
extends ChatModelTrait {
|
||||
|
||||
def hasMeetingEnded(): Boolean = {
|
||||
meetingModel.hasMeetingEnded()
|
||||
MeetingStatus2x.hasMeetingEnded(status)
|
||||
}
|
||||
|
||||
def webUserJoined() {
|
||||
if (Users.numWebUsers(users) > 0) {
|
||||
meetingModel.resetLastWebUserLeftOn()
|
||||
MeetingStatus2x.resetLastWebUserLeftOn(status)
|
||||
}
|
||||
}
|
||||
|
||||
def setCurrentPresenterInfo(pres: Presenter) {
|
||||
usersModel.setCurrentPresenterInfo(pres)
|
||||
}
|
||||
|
||||
def getCurrentPresenterInfo(): Presenter = {
|
||||
usersModel.getCurrentPresenterInfo()
|
||||
}
|
||||
|
||||
def addGlobalAudioConnection(userID: String): Boolean = {
|
||||
usersModel.addGlobalAudioConnection(userID)
|
||||
}
|
||||
|
||||
def removeGlobalAudioConnection(userID: String): Boolean = {
|
||||
usersModel.removeGlobalAudioConnection(userID)
|
||||
}
|
||||
|
||||
def startRecordingVoice() {
|
||||
usersModel.startRecordingVoice()
|
||||
}
|
||||
|
||||
def stopRecordingVoice() {
|
||||
usersModel.stopRecordingVoice()
|
||||
}
|
||||
|
||||
def isVoiceRecording: Boolean = {
|
||||
usersModel.isVoiceRecording
|
||||
}
|
||||
|
||||
def startCheckingIfWeNeedToEndVoiceConf() {
|
||||
if (Users.numWebUsers(users) == 0 && !mProps.isBreakout) {
|
||||
meetingModel.lastWebUserLeft()
|
||||
log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]")
|
||||
if (Users.numWebUsers(users) == 0 && !props.meetingProp.isBreakout) {
|
||||
MeetingStatus2x.lastWebUserLeft(status)
|
||||
}
|
||||
}
|
||||
|
||||
def sendTimeRemainingNotice() {
|
||||
val now = timeNowInSeconds
|
||||
|
||||
if (mProps.duration > 0 && (((meetingModel.startedOn + mProps.duration) - now) < 15)) {
|
||||
if (props.durationProps.duration > 0 && (((MeetingStatus2x.startedOn(status) + props.durationProps.duration) - now) < 15)) {
|
||||
// log.warning("MEETING WILL END IN 15 MINUTES!!!!")
|
||||
}
|
||||
}
|
||||
@ -87,15 +55,15 @@ class LiveMeeting(val mProps: MeetingProperties,
|
||||
}
|
||||
|
||||
def lockLayout(lock: Boolean) {
|
||||
meetingModel.lockLayout(lock)
|
||||
MeetingStatus2x.lockLayout(status, lock)
|
||||
}
|
||||
|
||||
def newPermissions(np: Permissions) {
|
||||
meetingModel.setPermissions(np)
|
||||
MeetingStatus2x.setPermissions(status, np)
|
||||
}
|
||||
|
||||
def permissionsEqual(other: Permissions): Boolean = {
|
||||
meetingModel.permissionsEqual(other)
|
||||
MeetingStatus2x.permissionsEqual(status, other)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,27 +2,30 @@ package org.bigbluebutton.core.running
|
||||
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor._
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.Props
|
||||
import akka.actor.OneForOneStrategy
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import akka.util.Timeout
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.common2.messages.MessageBody.ValidateAuthTokenRespMsgBody
|
||||
import org.bigbluebutton.common2.messages._
|
||||
import org.bigbluebutton.core._
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.models.Users
|
||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object MeetingActor {
|
||||
def props(mProps: MeetingProperties,
|
||||
def props(props: DefaultProps,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway, liveMeeting: LiveMeeting): Props =
|
||||
Props(classOf[MeetingActor], mProps, eventBus, outGW, liveMeeting)
|
||||
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
|
||||
}
|
||||
|
||||
class MeetingActor(val mProps: MeetingProperties,
|
||||
class MeetingActor(val props: DefaultProps,
|
||||
val eventBus: IncomingEventBus,
|
||||
val outGW: OutMessageGateway, val liveMeeting: LiveMeeting)
|
||||
extends Actor with ActorLogging
|
||||
@ -44,18 +47,27 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
* Put the internal message injector into another actor so this
|
||||
* actor is easy to test.
|
||||
*/
|
||||
var actorMonitor = context.actorOf(MeetingActorInternal.props(mProps, eventBus, outGW), "actorMonitor-" + mProps.meetingID)
|
||||
var actorMonitor = context.actorOf(MeetingActorInternal.props(props, eventBus, outGW),
|
||||
"actorMonitor-" + props.meetingProp.intId)
|
||||
|
||||
/** Subscribe to meeting and voice events. **/
|
||||
eventBus.subscribe(actorMonitor, mProps.meetingID)
|
||||
eventBus.subscribe(actorMonitor, mProps.voiceBridge)
|
||||
eventBus.subscribe(actorMonitor, mProps.deskshareBridge)
|
||||
eventBus.subscribe(actorMonitor, props.meetingProp.intId)
|
||||
eventBus.subscribe(actorMonitor, props.voiceProp.voiceConf)
|
||||
eventBus.subscribe(actorMonitor, props.screenshareProps.screenshareConf)
|
||||
|
||||
def receive = {
|
||||
//=============================
|
||||
// 2x messages
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
case msg: RegisterUserReqMsg => handleRegisterUserReqMsg(msg)
|
||||
//======================================
|
||||
|
||||
//=======================================
|
||||
// old messages
|
||||
case msg: ActivityResponse => handleActivityResponse(msg)
|
||||
case msg: MonitorNumberOfUsers => handleMonitorNumberOfUsers(msg)
|
||||
case msg: ValidateAuthToken => handleValidateAuthToken(msg)
|
||||
case msg: RegisterUser => handleRegisterUser(msg)
|
||||
//case msg: RegisterUser => handleRegisterUser(msg)
|
||||
case msg: UserJoinedVoiceConfMessage => handleUserJoinedVoiceConfMessage(msg)
|
||||
case msg: UserLeftVoiceConfMessage => handleUserLeftVoiceConfMessage(msg)
|
||||
case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg)
|
||||
@ -164,17 +176,68 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
msg.core match {
|
||||
case m: ValidateAuthTokenReqMsg => handleValidateAuthTokenReqMsg(m)
|
||||
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
||||
case _ => println("***** Cannot handle " + msg.envelope.name)
|
||||
}
|
||||
}
|
||||
|
||||
def handleRegisterUserReqMsg(msg: RegisterUserReqMsg): Unit = {
|
||||
log.debug("****** RECEIVED RegisterUserReqMsg msg {}", msg)
|
||||
if (MeetingStatus2x.hasMeetingEnded(liveMeeting.status)) {
|
||||
// Check first if the meeting has ended and the user refreshed the client to re-connect.
|
||||
log.info("Register user failed. Mmeeting has ended. meetingId=" + props.meetingProp.intId +
|
||||
" userId=" + msg.body.intUserId)
|
||||
} else {
|
||||
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
|
||||
msg.body.name, msg.body.role, msg.body.authToken,
|
||||
msg.body.avatarURL, msg.body.guest, msg.body.authed, msg.body.guest, liveMeeting.registeredUsers)
|
||||
|
||||
log.info("Register user success. meetingId=" + props.meetingProp.intId + " userId=" + msg.body.extUserId + " user=" + regUser)
|
||||
outGW.send(new UserRegistered(props.meetingProp.intId, props.recordProp.record, regUser))
|
||||
}
|
||||
}
|
||||
|
||||
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg): Unit = {
|
||||
log.debug("****** RECEIVED ValidateAuthTokenReqMsg msg {}", msg)
|
||||
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka",
|
||||
"msgType" -> "direct", "meetingId" -> props.meetingProp.intId, "userId" -> msg.body.userId)
|
||||
val envelope = BbbCoreEnvelope(ValidateAuthTokenRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(ValidateAuthTokenRespMsg.NAME, props.meetingProp.intId, msg.body.userId)
|
||||
|
||||
RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) match {
|
||||
case Some(u) =>
|
||||
log.info("ValidateToken success. meetingId=" + props.meetingProp.intId + " userId=" + msg.body.userId)
|
||||
|
||||
val body = ValidateAuthTokenRespMsgBody(msg.body.userId, msg.body.authToken, true)
|
||||
val event = ValidateAuthTokenRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
case None =>
|
||||
log.info("ValidateToken failed. meetingId=" + props.meetingProp.intId + " userId=" + msg.body.userId)
|
||||
val body = ValidateAuthTokenRespMsgBody(msg.body.userId, msg.body.authToken, false)
|
||||
val event = ValidateAuthTokenRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
}
|
||||
|
||||
def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest): Unit = {
|
||||
log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" + liveMeeting.meetingModel
|
||||
.isBroadcastingRTMP() + " URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
|
||||
log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" +
|
||||
MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status) + " URL:" +
|
||||
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
|
||||
|
||||
// only valid if currently broadcasting
|
||||
if (liveMeeting.meetingModel.isBroadcastingRTMP()) {
|
||||
if (MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
|
||||
log.info("STOP broadcast ALLOWED when isBroadcastingRTMP=true")
|
||||
liveMeeting.meetingModel.broadcastingRTMPStopped()
|
||||
MeetingStatus2x.broadcastingRTMPStopped(liveMeeting.status)
|
||||
|
||||
// notify viewers that RTMP broadcast stopped
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, liveMeeting.meetingModel.getRTMPBroadcastingUrl(),
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(props.meetingProp.intId,
|
||||
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status),
|
||||
msg.videoWidth, msg.videoHeight, false))
|
||||
} else {
|
||||
log.info("STOP broadcast NOT ALLOWED when isBroadcastingRTMP=false")
|
||||
@ -184,42 +247,48 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
|
||||
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting="
|
||||
+ liveMeeting.meetingModel.isBroadcastingRTMP() + " URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
|
||||
if (liveMeeting.meetingModel.isBroadcastingRTMP()) {
|
||||
+ MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status) + " URL:" +
|
||||
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
|
||||
|
||||
if (MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
|
||||
// if the meeting has an ongoing WebRTC Deskshare session, send a notification
|
||||
outGW.send(new DeskShareNotifyASingleViewer(mProps.meetingID, msg.requesterID, liveMeeting.meetingModel.getRTMPBroadcastingUrl(),
|
||||
liveMeeting.meetingModel.getDesktopShareVideoWidth(), liveMeeting.meetingModel.getDesktopShareVideoHeight(), true))
|
||||
outGW.send(new DeskShareNotifyASingleViewer(props.meetingProp.intId, msg.requesterID,
|
||||
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status),
|
||||
MeetingStatus2x.getDesktopShareVideoWidth(liveMeeting.status),
|
||||
MeetingStatus2x.getDesktopShareVideoHeight(liveMeeting.status), true))
|
||||
}
|
||||
}
|
||||
|
||||
def handleGetGuestPolicy(msg: GetGuestPolicy) {
|
||||
outGW.send(new GetGuestPolicyReply(msg.meetingID, mProps.recorded, msg.requesterID, liveMeeting.meetingModel.getGuestPolicy().toString()))
|
||||
outGW.send(new GetGuestPolicyReply(msg.meetingID, props.recordProp.record,
|
||||
msg.requesterID, MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString()))
|
||||
}
|
||||
|
||||
def handleSetGuestPolicy(msg: SetGuestPolicy) {
|
||||
liveMeeting.meetingModel.setGuestPolicy(msg.policy)
|
||||
liveMeeting.meetingModel.setGuestPolicySetBy(msg.setBy)
|
||||
outGW.send(new GuestPolicyChanged(msg.meetingID, mProps.recorded, liveMeeting.meetingModel.getGuestPolicy().toString()))
|
||||
MeetingStatus2x.setGuestPolicy(liveMeeting.status, msg.policy)
|
||||
MeetingStatus2x.setGuestPolicySetBy(liveMeeting.status, msg.setBy)
|
||||
outGW.send(new GuestPolicyChanged(msg.meetingID, props.recordProp.record,
|
||||
MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString()))
|
||||
}
|
||||
|
||||
def handleLogoutEndMeeting(msg: LogoutEndMeeting) {
|
||||
if (Users.isModerator(msg.userID, liveMeeting.users)) {
|
||||
handleEndMeeting(EndMeeting(mProps.meetingID))
|
||||
handleEndMeeting(EndMeeting(props.meetingProp.intId))
|
||||
}
|
||||
}
|
||||
|
||||
def handleActivityResponse(msg: ActivityResponse) {
|
||||
log.info("User endorsed that meeting {} is active", mProps.meetingID)
|
||||
outGW.send(new MeetingIsActive(mProps.meetingID))
|
||||
log.info("User endorsed that meeting {} is active", props.meetingProp.intId)
|
||||
outGW.send(new MeetingIsActive(props.meetingProp.intId))
|
||||
}
|
||||
|
||||
def handleEndMeeting(msg: EndMeeting) {
|
||||
// Broadcast users the meeting will end
|
||||
outGW.send(new MeetingEnding(msg.meetingId))
|
||||
|
||||
liveMeeting.meetingModel.meetingHasEnded
|
||||
MeetingStatus2x.meetingHasEnded(liveMeeting.status)
|
||||
|
||||
outGW.send(new MeetingEnded(msg.meetingId, mProps.recorded, mProps.voiceBridge))
|
||||
outGW.send(new MeetingEnded(msg.meetingId, props.recordProp.record, props.meetingProp.intId))
|
||||
}
|
||||
|
||||
def handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = {
|
||||
@ -234,69 +303,75 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
|
||||
def handleVoiceConfRecordingStartedMessage(msg: VoiceConfRecordingStartedMessage) {
|
||||
if (msg.recording) {
|
||||
liveMeeting.meetingModel.setVoiceRecordingFilename(msg.recordStream)
|
||||
outGW.send(new VoiceRecordingStarted(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
|
||||
MeetingStatus2x.setVoiceRecordingFilename(liveMeeting.status, msg.recordStream)
|
||||
outGW.send(new VoiceRecordingStarted(props.meetingProp.intId, props.recordProp.record,
|
||||
msg.recordStream, msg.timestamp, props.voiceProp.voiceConf))
|
||||
} else {
|
||||
liveMeeting.meetingModel.setVoiceRecordingFilename("")
|
||||
outGW.send(new VoiceRecordingStopped(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
|
||||
MeetingStatus2x.setVoiceRecordingFilename(liveMeeting.status, "")
|
||||
outGW.send(new VoiceRecordingStopped(props.meetingProp.intId, props.recordProp.record,
|
||||
msg.recordStream, msg.timestamp, props.voiceProp.voiceConf))
|
||||
}
|
||||
}
|
||||
|
||||
def handleSetRecordingStatus(msg: SetRecordingStatus) {
|
||||
log.info("Change recording status. meetingId=" + mProps.meetingID + " recording=" + msg.recording)
|
||||
if (mProps.allowStartStopRecording && liveMeeting.meetingModel.isRecording() != msg.recording) {
|
||||
log.info("Change recording status. meetingId=" + props.meetingProp.intId + " recording=" + msg.recording)
|
||||
if (props.recordProp.allowStartStopRecording &&
|
||||
MeetingStatus2x.isRecording(liveMeeting.status) != msg.recording) {
|
||||
if (msg.recording) {
|
||||
liveMeeting.meetingModel.recordingStarted()
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
} else {
|
||||
liveMeeting.meetingModel.recordingStopped()
|
||||
MeetingStatus2x.recordingStopped(liveMeeting.status)
|
||||
}
|
||||
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, msg.userId, msg.recording))
|
||||
outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record, msg.userId, msg.recording))
|
||||
}
|
||||
}
|
||||
|
||||
// WebRTC Desktop Sharing
|
||||
|
||||
def handleDeskShareStartedRequest(msg: DeskShareStartedRequest): Unit = {
|
||||
log.info("handleDeskShareStartedRequest: dsStarted=" + liveMeeting.meetingModel.getDeskShareStarted())
|
||||
log.info("handleDeskShareStartedRequest: dsStarted=" + MeetingStatus2x.getDeskShareStarted(liveMeeting.status))
|
||||
|
||||
if (!liveMeeting.meetingModel.getDeskShareStarted()) {
|
||||
if (!MeetingStatus2x.getDeskShareStarted(liveMeeting.status)) {
|
||||
val timestamp = System.currentTimeMillis().toString
|
||||
val streamPath = "rtmp://" + mProps.red5DeskShareIP + "/" + mProps.red5DeskShareApp +
|
||||
"/" + mProps.meetingID + "/" + mProps.meetingID + "-" + timestamp
|
||||
val streamPath = "rtmp://" + props.screenshareProps.red5ScreenshareIp + "/" + props.screenshareProps.red5ScreenshareApp +
|
||||
"/" + props.meetingProp.intId + "/" + props.meetingProp.intId + "-" + timestamp
|
||||
log.info("handleDeskShareStartedRequest: streamPath=" + streamPath)
|
||||
|
||||
// Tell FreeSwitch to broadcast to RTMP
|
||||
outGW.send(new DeskShareStartRTMPBroadcast(msg.conferenceName, streamPath))
|
||||
|
||||
liveMeeting.meetingModel.setDeskShareStarted(true)
|
||||
MeetingStatus2x.setDeskShareStarted(liveMeeting.status, true)
|
||||
}
|
||||
}
|
||||
|
||||
def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest): Unit = {
|
||||
log.info("handleDeskShareStoppedRequest: dsStarted=" + liveMeeting.meetingModel.getDeskShareStarted() +
|
||||
" URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
|
||||
log.info("handleDeskShareStoppedRequest: dsStarted=" +
|
||||
MeetingStatus2x.getDeskShareStarted(liveMeeting.status) +
|
||||
" URL:" + MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
|
||||
|
||||
// Tell FreeSwitch to stop broadcasting to RTMP
|
||||
outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName, liveMeeting.meetingModel.getRTMPBroadcastingUrl()))
|
||||
outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName,
|
||||
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status)))
|
||||
|
||||
liveMeeting.meetingModel.setDeskShareStarted(false)
|
||||
MeetingStatus2x.setDeskShareStarted(liveMeeting.status, false)
|
||||
}
|
||||
|
||||
def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest): Unit = {
|
||||
log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" + liveMeeting.meetingModel.isBroadcastingRTMP() +
|
||||
" URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
|
||||
log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" +
|
||||
MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status) +
|
||||
" URL:" + MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
|
||||
|
||||
// only valid if not broadcasting yet
|
||||
if (!liveMeeting.meetingModel.isBroadcastingRTMP()) {
|
||||
liveMeeting.meetingModel.setRTMPBroadcastingUrl(msg.streamname)
|
||||
liveMeeting.meetingModel.broadcastingRTMPStarted()
|
||||
liveMeeting.meetingModel.setDesktopShareVideoWidth(msg.videoWidth)
|
||||
liveMeeting.meetingModel.setDesktopShareVideoHeight(msg.videoHeight)
|
||||
if (!MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
|
||||
MeetingStatus2x.setRTMPBroadcastingUrl(liveMeeting.status, msg.streamname)
|
||||
MeetingStatus2x.broadcastingRTMPStarted(liveMeeting.status)
|
||||
MeetingStatus2x.setDesktopShareVideoWidth(liveMeeting.status, msg.videoWidth)
|
||||
MeetingStatus2x.setDesktopShareVideoHeight(liveMeeting.status, msg.videoHeight)
|
||||
log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
|
||||
|
||||
// Notify viewers in the meeting that there's an rtmp stream to view
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, msg.streamname, msg.videoWidth, msg.videoHeight, true))
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(props.meetingProp.intId, msg.streamname, msg.videoWidth, msg.videoHeight, true))
|
||||
} else {
|
||||
log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
|
||||
}
|
||||
@ -308,10 +383,11 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
}
|
||||
|
||||
def monitorNumberOfWebUsers() {
|
||||
if (Users.numWebUsers(liveMeeting.users) == 0 && liveMeeting.meetingModel.lastWebUserLeftOn > 0) {
|
||||
if (liveMeeting.timeNowInMinutes - liveMeeting.meetingModel.lastWebUserLeftOn > 2) {
|
||||
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", mProps.meetingID)
|
||||
outGW.send(new EjectAllVoiceUsers(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
if (Users.numWebUsers(liveMeeting.users) == 0 &&
|
||||
MeetingStatus2x.lastWebUserLeftOn(liveMeeting.status) > 0) {
|
||||
if (liveMeeting.timeNowInMinutes - MeetingStatus2x.lastWebUserLeftOn(liveMeeting.status) > 2) {
|
||||
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", props.meetingProp.intId)
|
||||
outGW.send(new EjectAllVoiceUsers(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -319,23 +395,24 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
def monitorNumberOfUsers() {
|
||||
val hasUsers = Users.numUsers(liveMeeting.users) != 0
|
||||
// TODO: We could use a better control over this message to send it just when it really matters :)
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, UpdateMeetingExpireMonitor(mProps.meetingID, hasUsers)))
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, UpdateMeetingExpireMonitor(props.meetingProp.intId, hasUsers)))
|
||||
}
|
||||
|
||||
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate) {
|
||||
if (mProps.duration > 0) {
|
||||
val endMeetingTime = liveMeeting.meetingModel.startedOn + (mProps.duration * 60)
|
||||
if (props.durationProps.duration > 0) {
|
||||
val endMeetingTime = MeetingStatus2x.startedOn(liveMeeting.status) + (props.durationProps.duration * 60)
|
||||
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds
|
||||
outGW.send(new MeetingTimeRemainingUpdate(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
|
||||
outGW.send(new MeetingTimeRemainingUpdate(props.meetingProp.intId, props.recordProp.record, timeRemaining.toInt))
|
||||
}
|
||||
if (!mProps.isBreakout && liveMeeting.breakoutModel.getRooms().length > 0) {
|
||||
if (!props.meetingProp.isBreakout && liveMeeting.breakoutModel.getRooms().length > 0) {
|
||||
val room = liveMeeting.breakoutModel.getRooms()(0);
|
||||
val endMeetingTime = liveMeeting.meetingModel.breakoutRoomsStartedOn + (liveMeeting.meetingModel.breakoutRoomsdurationInMinutes * 60)
|
||||
val endMeetingTime = MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status) +
|
||||
(MeetingStatus2x.breakoutRoomsdurationInMinutes(liveMeeting.status) * 60)
|
||||
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds
|
||||
outGW.send(new BreakoutRoomsTimeRemainingUpdateOutMessage(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
|
||||
} else if (liveMeeting.meetingModel.breakoutRoomsStartedOn != 0) {
|
||||
liveMeeting.meetingModel.breakoutRoomsdurationInMinutes = 0;
|
||||
liveMeeting.meetingModel.breakoutRoomsStartedOn = 0;
|
||||
outGW.send(new BreakoutRoomsTimeRemainingUpdateOutMessage(props.meetingProp.intId, props.recordProp.record, timeRemaining.toInt))
|
||||
} else if (MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status) != 0) {
|
||||
MeetingStatus2x.breakoutRoomsdurationInMinutes(liveMeeting.status, 0)
|
||||
MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,29 +421,32 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
}
|
||||
|
||||
def handleGetRecordingStatus(msg: GetRecordingStatus) {
|
||||
outGW.send(new GetRecordingStatusReply(mProps.meetingID, mProps.recorded, msg.userId, liveMeeting.meetingModel.isRecording().booleanValue()))
|
||||
outGW.send(new GetRecordingStatusReply(props.meetingProp.intId, props.recordProp.record,
|
||||
msg.userId, MeetingStatus2x.isRecording(liveMeeting.status).booleanValue()))
|
||||
}
|
||||
|
||||
def startRecordingIfAutoStart() {
|
||||
if (mProps.recorded && !liveMeeting.meetingModel.isRecording() &&
|
||||
mProps.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 1) {
|
||||
log.info("Auto start recording. meetingId={}", mProps.meetingID)
|
||||
liveMeeting.meetingModel.recordingStarted()
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", liveMeeting.meetingModel.isRecording()))
|
||||
if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
|
||||
props.recordProp.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 1) {
|
||||
log.info("Auto start recording. meetingId={}", props.meetingProp.intId)
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record,
|
||||
"system", MeetingStatus2x.isRecording(liveMeeting.status)))
|
||||
}
|
||||
}
|
||||
|
||||
def stopAutoStartedRecording() {
|
||||
if (mProps.recorded && liveMeeting.meetingModel.isRecording() &&
|
||||
mProps.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 0) {
|
||||
log.info("Last web user left. Auto stopping recording. meetingId={}", mProps.meetingID)
|
||||
liveMeeting.meetingModel.recordingStopped()
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", liveMeeting.meetingModel.isRecording()))
|
||||
if (props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
|
||||
props.recordProp.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 0) {
|
||||
log.info("Last web user left. Auto stopping recording. meetingId={}", props.meetingProp.intId)
|
||||
MeetingStatus2x.recordingStopped(liveMeeting.status)
|
||||
outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record,
|
||||
"system", MeetingStatus2x.isRecording(liveMeeting.status)))
|
||||
}
|
||||
}
|
||||
|
||||
def sendMeetingHasEnded(userId: String) {
|
||||
outGW.send(new MeetingHasEnded(mProps.meetingID, userId))
|
||||
outGW.send(new DisconnectUser(mProps.meetingID, userId))
|
||||
outGW.send(new MeetingHasEnded(props.meetingProp.intId, userId))
|
||||
outGW.send(new DisconnectUser(props.meetingProp.intId, userId))
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,33 @@
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.Props
|
||||
import akka.actor.OneForOneStrategy
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.core.{ MeetingProperties, OutMessageGateway }
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.core.{ OutMessageGateway }
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus }
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration.{ Deadline, FiniteDuration }
|
||||
|
||||
object MeetingActorInternal {
|
||||
def props(mProps: MeetingProperties,
|
||||
def props(props: DefaultProps,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway): Props =
|
||||
Props(classOf[MeetingActorInternal], mProps, eventBus, outGW)
|
||||
Props(classOf[MeetingActorInternal], props, eventBus, outGW)
|
||||
}
|
||||
|
||||
// This actor is an internal audit actor for each meeting actor that
|
||||
// periodically sends messages to the meeting actor
|
||||
class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
class MeetingActorInternal(val props: DefaultProps,
|
||||
val eventBus: IncomingEventBus, val outGW: OutMessageGateway)
|
||||
extends Actor with ActorLogging with SystemConfiguration {
|
||||
|
||||
@ -38,7 +42,7 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
}
|
||||
|
||||
private def getInactivityDeadline(): Int = {
|
||||
val time = getMetadata(Metadata.INACTIVITY_DEADLINE, mProps.metadata) match {
|
||||
val time = getMetadata(Metadata.INACTIVITY_DEADLINE, props.metadataProp.metadata) match {
|
||||
case Some(result) => result.asInstanceOf[Int]
|
||||
case None => inactivityDeadline
|
||||
}
|
||||
@ -47,7 +51,7 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
}
|
||||
|
||||
private def getInactivityTimeLeft(): Int = {
|
||||
val time = getMetadata(Metadata.INACTIVITY_TIMELEFT, mProps.metadata) match {
|
||||
val time = getMetadata(Metadata.INACTIVITY_TIMELEFT, props.metadataProp.metadata) match {
|
||||
case Some(result) => result.asInstanceOf[Int]
|
||||
case None => inactivityTimeLeft
|
||||
}
|
||||
@ -74,7 +78,7 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
private var inactivity = InactivityDeadline.fromNow
|
||||
private var inactivityWarning: Deadline = null
|
||||
|
||||
private val ExpireMeetingDuration = FiniteDuration(mProps.duration, "minutes")
|
||||
private val ExpireMeetingDuration = FiniteDuration(props.durationProps.duration, "minutes")
|
||||
private val ExpireMeetingNeverJoined = FiniteDuration(getExpireNeverJoined(), "seconds")
|
||||
private val ExpireMeetingLastUserLeft = FiniteDuration(getExpireLastUserLeft(), "seconds")
|
||||
private var meetingExpire = ExpireMeetingNeverJoined.fromNow
|
||||
@ -84,13 +88,14 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
context.system.scheduler.schedule(5 seconds, MonitorFrequency, self, "Monitor")
|
||||
|
||||
// Query to get voice conference users
|
||||
outGW.send(new GetUsersInVoiceConference(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
outGW.send(new GetUsersInVoiceConference(props.meetingProp.intId, props.recordProp.record,
|
||||
props.voiceProp.voiceConf))
|
||||
|
||||
if (mProps.isBreakout) {
|
||||
if (props.meetingProp.isBreakout) {
|
||||
// This is a breakout room. Inform our parent meeting that we have been successfully created.
|
||||
eventBus.publish(BigBlueButtonEvent(
|
||||
mProps.parentMeetingID,
|
||||
BreakoutRoomCreated(mProps.parentMeetingID, mProps.meetingID)))
|
||||
props.breakoutProps.parentId,
|
||||
BreakoutRoomCreated(props.breakoutProps.parentId, props.meetingProp.intId)))
|
||||
}
|
||||
|
||||
def receive = {
|
||||
@ -112,27 +117,27 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
}
|
||||
|
||||
def handleMonitorNumberOfWebUsers() {
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, MonitorNumberOfUsers(mProps.meetingID)))
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsers(props.meetingProp.intId)))
|
||||
|
||||
// Trigger updating users of time remaining on meeting.
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, SendTimeRemainingUpdate(mProps.meetingID)))
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingUpdate(props.meetingProp.intId)))
|
||||
|
||||
if (mProps.isBreakout) {
|
||||
if (props.meetingProp.isBreakout) {
|
||||
// This is a breakout room. Update the main meeting with list of users in this breakout room.
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, SendBreakoutUsersUpdate(mProps.meetingID)))
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendBreakoutUsersUpdate(props.meetingProp.intId)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def handleMonitorActivity() {
|
||||
if (inactivity.isOverdue() && inactivityWarning != null && inactivityWarning.isOverdue()) {
|
||||
log.info("Closing meeting {} due to inactivity for {} seconds", mProps.meetingID, InactivityDeadline.toSeconds)
|
||||
log.info("Closing meeting {} due to inactivity for {} seconds", props.meetingProp.intId, InactivityDeadline.toSeconds)
|
||||
updateInactivityMonitors()
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, EndMeeting(mProps.meetingID)))
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
|
||||
// Or else make sure to send only one warning message
|
||||
} else if (inactivity.isOverdue() && inactivityWarning == null) {
|
||||
log.info("Sending inactivity warning to meeting {}", mProps.meetingID)
|
||||
outGW.send(new InactivityWarning(mProps.meetingID, InactivityTimeLeft.toSeconds))
|
||||
log.info("Sending inactivity warning to meeting {}", props.meetingProp.intId)
|
||||
outGW.send(new InactivityWarning(props.meetingProp.intId, InactivityTimeLeft.toSeconds))
|
||||
// We add 5 seconds so clients will have enough time to process the message
|
||||
inactivityWarning = (InactivityTimeLeft + (5 seconds)).fromNow
|
||||
}
|
||||
@ -141,14 +146,14 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
private def handleMonitorExpiration() {
|
||||
if (meetingExpire != null && meetingExpire.isOverdue()) {
|
||||
// User related meeting expiration methods
|
||||
log.debug("Meeting {} expired. No users", mProps.meetingID)
|
||||
log.debug("Meeting {} expired. No users", props.meetingProp.intId)
|
||||
meetingExpire = null
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, EndMeeting(mProps.meetingID)))
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
|
||||
} else if (meetingDuration != null && meetingDuration.isOverdue()) {
|
||||
// Default meeting duration
|
||||
meetingDuration = null
|
||||
log.debug("Meeting {} expired. Reached it's fixed duration of {}", mProps.meetingID, ExpireMeetingDuration.toString())
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, EndMeeting(mProps.meetingID)))
|
||||
log.debug("Meeting {} expired. Reached it's fixed duration of {}", props.meetingProp.intId, ExpireMeetingDuration.toString())
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,13 +161,13 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
if (msg.hasUser) {
|
||||
if (meetingExpire != null) {
|
||||
// User joined. Forget about this expiration for now
|
||||
log.debug("Meeting has users. Stopping expiration for meeting {}", mProps.meetingID)
|
||||
log.debug("Meeting has users. Stopping expiration for meeting {}", props.meetingProp.intId)
|
||||
meetingExpire = null
|
||||
}
|
||||
} else {
|
||||
if (meetingExpire == null) {
|
||||
// User list is empty. Start this meeting expiration method
|
||||
log.debug("Meeting has no users. Starting {} expiration for meeting {}", ExpireMeetingLastUserLeft.toString(), mProps.meetingID)
|
||||
log.debug("Meeting has no users. Starting {} expiration for meeting {}", ExpireMeetingLastUserLeft.toString(), props.meetingProp.intId)
|
||||
meetingExpire = ExpireMeetingLastUserLeft.fromNow
|
||||
}
|
||||
}
|
||||
@ -175,16 +180,16 @@ class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
|
||||
private def notifyActivity() {
|
||||
if (inactivityWarning != null) {
|
||||
outGW.send(new MeetingIsActive(mProps.meetingID))
|
||||
outGW.send(new MeetingIsActive(props.meetingProp.intId))
|
||||
}
|
||||
|
||||
updateInactivityMonitors()
|
||||
}
|
||||
|
||||
private def handleActivityResponse(msg: ActivityResponse) {
|
||||
log.info("User endorsed that meeting {} is active", mProps.meetingID)
|
||||
log.info("User endorsed that meeting {} is active", props.meetingProp.intId)
|
||||
updateInactivityMonitors()
|
||||
outGW.send(new MeetingIsActive(mProps.meetingID))
|
||||
outGW.send(new MeetingIsActive(props.meetingProp.intId))
|
||||
}
|
||||
|
||||
private def isMeetingActivity(msg: Object): Boolean = {
|
||||
|
@ -1,25 +1,24 @@
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorContext
|
||||
import org.bigbluebutton.common2.domain.{ DefaultProps, Meeting2x }
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
|
||||
import org.bigbluebutton.core.{ MeetingModel, MeetingProperties, OutMessageGateway }
|
||||
import org.bigbluebutton.core.{ OutMessageGateway }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
object RunningMeeting {
|
||||
def apply(mProps: MeetingProperties, outGW: OutMessageGateway,
|
||||
def apply(props: DefaultProps, outGW: OutMessageGateway,
|
||||
eventBus: IncomingEventBus)(implicit context: ActorContext) =
|
||||
new RunningMeeting(mProps, outGW, eventBus)(context)
|
||||
new RunningMeeting(props, outGW, eventBus)(context)
|
||||
}
|
||||
|
||||
class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway,
|
||||
class RunningMeeting(val props: DefaultProps, val outGW: OutMessageGateway,
|
||||
val eventBus: IncomingEventBus)(implicit val context: ActorContext) {
|
||||
|
||||
val chatModel = new ChatModel()
|
||||
val layoutModel = new LayoutModel()
|
||||
val meetingModel = new MeetingModel()
|
||||
val usersModel = new UsersModel()
|
||||
val pollModel = new PollModel()
|
||||
val wbModel = new WhiteboardModel()
|
||||
val presModel = new PresentationModel()
|
||||
@ -28,15 +27,16 @@ class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway
|
||||
val notesModel = new SharedNotesModel()
|
||||
val users = new Users
|
||||
val registeredUsers = new RegisteredUsers
|
||||
val meetingStatux2x = new MeetingStatus2x
|
||||
|
||||
meetingModel.setGuestPolicy(mProps.guestPolicy)
|
||||
// meetingModel.setGuestPolicy(props.usersProp.guestPolicy)
|
||||
|
||||
// We extract the meeting handlers into this class so it is
|
||||
// easy to test.
|
||||
val liveMeeting = new LiveMeeting(mProps,
|
||||
chatModel, layoutModel, meetingModel, usersModel, users, registeredUsers, pollModel,
|
||||
val liveMeeting = new LiveMeeting(props, meetingStatux2x, chatModel, layoutModel,
|
||||
users, registeredUsers, pollModel,
|
||||
wbModel, presModel, breakoutModel, captionModel, notesModel)
|
||||
|
||||
val actorRef = context.actorOf(MeetingActor.props(mProps, eventBus, outGW, liveMeeting), mProps.meetingID)
|
||||
val actorRef = context.actorOf(MeetingActor.props(props, eventBus, outGW, liveMeeting), props.meetingProp.intId)
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package org.bigbluebutton.core2
|
||||
|
||||
import akka.actor.{ Actor, ActorLogging, Props }
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common2.messages.BbbCommonEnvCoreMsg
|
||||
import org.bigbluebutton.common2.util.JsonUtil
|
||||
import org.bigbluebutton.core.MessageSender
|
||||
import org.bigbluebutton.core.bus.BbbOutMessage
|
||||
|
||||
object FromAkkaAppsMsgSenderActor {
|
||||
def props(msgSender: MessageSender): Props = Props(classOf[FromAkkaAppsMsgSenderActor], msgSender)
|
||||
}
|
||||
|
||||
class FromAkkaAppsMsgSenderActor(msgSender: MessageSender) extends Actor with ActorLogging with SystemConfiguration {
|
||||
|
||||
def receive = {
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
case _ => println("************* FromAkkaAppsMsgSenderActor Cannot handle message ")
|
||||
}
|
||||
|
||||
def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
val json = JsonUtil.toJson(msg)
|
||||
println("****** Publishing " + json)
|
||||
msgSender.send(fromAkkaAppsRedisChannel, json)
|
||||
}
|
||||
}
|
339
akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/MeetingStatus2x.scala
Executable file
339
akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/MeetingStatus2x.scala
Executable file
@ -0,0 +1,339 @@
|
||||
package org.bigbluebutton.core2
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.bigbluebutton.core.MeetingExtensionProp
|
||||
import org.bigbluebutton.core.api.{ GuestPolicy, Permissions, Presenter }
|
||||
|
||||
object MeetingStatus2x {
|
||||
def setCurrentPresenterInfo(status: MeetingStatus2x, pres: Presenter) {
|
||||
status.currentPresenter = pres
|
||||
}
|
||||
|
||||
def getCurrentPresenterInfo(status: MeetingStatus2x): Presenter = {
|
||||
status.currentPresenter
|
||||
}
|
||||
|
||||
def addGlobalAudioConnection(status: MeetingStatus2x, userID: String): Boolean = {
|
||||
status.globalAudioConnectionCounter.get(userID) match {
|
||||
case Some(vc) => {
|
||||
status.globalAudioConnectionCounter += userID -> (vc + 1)
|
||||
false
|
||||
}
|
||||
case None => {
|
||||
status.globalAudioConnectionCounter += userID -> 1
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def removeGlobalAudioConnection(status: MeetingStatus2x, userID: String): Boolean = {
|
||||
status.globalAudioConnectionCounter.get(userID) match {
|
||||
case Some(vc) => {
|
||||
if (vc == 1) {
|
||||
status.globalAudioConnectionCounter -= userID
|
||||
true
|
||||
} else {
|
||||
status.globalAudioConnectionCounter += userID -> (vc - 1)
|
||||
false
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingVoice(status: MeetingStatus2x): Boolean = {
|
||||
status.recordingVoice = true
|
||||
status.recordingVoice
|
||||
}
|
||||
|
||||
def stopRecordingVoice(status: MeetingStatus2x): Boolean = {
|
||||
status.recordingVoice = false
|
||||
status.recordingVoice
|
||||
}
|
||||
|
||||
def isVoiceRecording(status: MeetingStatus2x): Boolean = {
|
||||
status.recordingVoice
|
||||
}
|
||||
|
||||
def resetDesktopSharingParams(status: MeetingStatus2x) = {
|
||||
status.broadcastingRTMP = false
|
||||
status.deskShareStarted = false
|
||||
status.rtmpBroadcastingUrl = ""
|
||||
status.desktopShareVideoWidth = 0
|
||||
status.desktopShareVideoHeight = 0
|
||||
}
|
||||
|
||||
def getDeskShareStarted(status: MeetingStatus2x): Boolean = {
|
||||
return status.deskShareStarted
|
||||
}
|
||||
|
||||
def setDeskShareStarted(status: MeetingStatus2x, b: Boolean) {
|
||||
status.deskShareStarted = b
|
||||
}
|
||||
|
||||
def setDesktopShareVideoWidth(status: MeetingStatus2x, videoWidth: Int) {
|
||||
status.desktopShareVideoWidth = videoWidth
|
||||
}
|
||||
|
||||
def setDesktopShareVideoHeight(status: MeetingStatus2x, videoHeight: Int) {
|
||||
status.desktopShareVideoHeight = videoHeight
|
||||
}
|
||||
|
||||
def getDesktopShareVideoWidth(status: MeetingStatus2x): Int = {
|
||||
status.desktopShareVideoWidth
|
||||
}
|
||||
|
||||
def getDesktopShareVideoHeight(status: MeetingStatus2x): Int = {
|
||||
status.desktopShareVideoHeight
|
||||
}
|
||||
|
||||
def broadcastingRTMPStarted(status: MeetingStatus2x) {
|
||||
status.broadcastingRTMP = true
|
||||
}
|
||||
|
||||
def isBroadcastingRTMP(status: MeetingStatus2x): Boolean = {
|
||||
status.broadcastingRTMP
|
||||
}
|
||||
|
||||
def broadcastingRTMPStopped(status: MeetingStatus2x) {
|
||||
status.broadcastingRTMP = false
|
||||
}
|
||||
|
||||
def setRTMPBroadcastingUrl(status: MeetingStatus2x, path: String) {
|
||||
status.rtmpBroadcastingUrl = path
|
||||
}
|
||||
|
||||
def getRTMPBroadcastingUrl(status: MeetingStatus2x): String = {
|
||||
status.rtmpBroadcastingUrl
|
||||
}
|
||||
|
||||
def isExtensionAllowed(status: MeetingStatus2x): Boolean = status.extension.numExtensions < status.extension.maxExtensions
|
||||
def incNumExtension(status: MeetingStatus2x): Int = {
|
||||
if (status.extension.numExtensions < status.extension.maxExtensions) {
|
||||
status.extension = status.extension.copy(numExtensions = status.extension.numExtensions + 1); status.extension.numExtensions
|
||||
}
|
||||
status.extension.numExtensions
|
||||
}
|
||||
|
||||
def notice15MinutesSent(status: MeetingStatus2x) = status.extension = status.extension.copy(sent15MinNotice = true)
|
||||
def notice10MinutesSent(status: MeetingStatus2x) = status.extension = status.extension.copy(sent10MinNotice = true)
|
||||
def notice5MinutesSent(status: MeetingStatus2x) = status.extension = status.extension.copy(sent5MinNotice = true)
|
||||
|
||||
def getMeetingExtensionProp(status: MeetingStatus2x): MeetingExtensionProp = status.extension
|
||||
def muteMeeting(status: MeetingStatus2x) = status.meetingMuted = true
|
||||
def unmuteMeeting(status: MeetingStatus2x) = status.meetingMuted = false
|
||||
def isMeetingMuted(status: MeetingStatus2x): Boolean = status.meetingMuted
|
||||
def recordingStarted(status: MeetingStatus2x) = status.recording = true
|
||||
def recordingStopped(status: MeetingStatus2x) = status.recording = false
|
||||
def isRecording(status: MeetingStatus2x): Boolean = status.recording
|
||||
def lastWebUserLeft(status: MeetingStatus2x) = status.lastWebUserLeftOnTimestamp = MeetingStatus2x.timeNowInMinutes
|
||||
def lastWebUserLeftOn(status: MeetingStatus2x): Long = status.lastWebUserLeftOnTimestamp
|
||||
def resetLastWebUserLeftOn(status: MeetingStatus2x) = status.lastWebUserLeftOnTimestamp = 0
|
||||
def setVoiceRecordingFilename(status: MeetingStatus2x, path: String) = status.voiceRecordingFilename = path
|
||||
def getVoiceRecordingFilename(status: MeetingStatus2x): String = status.voiceRecordingFilename
|
||||
def permisionsInitialized(status: MeetingStatus2x): Boolean = status.permissionsInited
|
||||
def initializePermissions(status: MeetingStatus2x) = status.permissionsInited = true
|
||||
def audioSettingsInitialized(status: MeetingStatus2x): Boolean = status.audioSettingsInited
|
||||
def initializeAudioSettings(status: MeetingStatus2x) = status.audioSettingsInited = true
|
||||
def permissionsEqual(status: MeetingStatus2x, other: Permissions): Boolean = status.permissions == other
|
||||
def lockLayout(status: MeetingStatus2x, lock: Boolean) = status.permissions = status.permissions.copy(lockedLayout = lock)
|
||||
def getPermissions(status: MeetingStatus2x): Permissions = status.permissions
|
||||
def setPermissions(status: MeetingStatus2x, p: Permissions) = status.permissions = p
|
||||
def meetingHasEnded(status: MeetingStatus2x) = status.meetingEnded = true
|
||||
def hasMeetingEnded(status: MeetingStatus2x): Boolean = status.meetingEnded
|
||||
def timeNowInMinutes(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
def timeNowInSeconds(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
|
||||
def getGuestPolicy(status: MeetingStatus2x): GuestPolicy.GuestPolicy = status.guestPolicy
|
||||
def setGuestPolicy(status: MeetingStatus2x, policy: GuestPolicy.GuestPolicy) = status.guestPolicy = policy
|
||||
def getGuestPolicySetBy(status: MeetingStatus2x): String = status.guestPolicySetBy
|
||||
def setGuestPolicySetBy(status: MeetingStatus2x, user: String) = status.guestPolicySetBy = user
|
||||
|
||||
def startedOn(status: MeetingStatus2x): Long = status.startedOn
|
||||
|
||||
def breakoutRoomsStartedOn(status: MeetingStatus2x) = status.breakoutRoomsStartedOn
|
||||
def breakoutRoomsStartedOn(status: MeetingStatus2x, startedOn: Long) = status.breakoutRoomsStartedOn = startedOn
|
||||
|
||||
def breakoutRoomsdurationInMinutes(status: MeetingStatus2x) = status.breakoutRoomsdurationInMinutes
|
||||
def breakoutRoomsdurationInMinutes(status: MeetingStatus2x, duration: Int) = status.breakoutRoomsdurationInMinutes = duration
|
||||
|
||||
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
|
||||
}
|
||||
|
||||
class MeetingStatus2x {
|
||||
private var globalAudioConnectionCounter = new collection.immutable.HashMap[String, Integer]
|
||||
|
||||
private var recordingVoice = false
|
||||
|
||||
private var currentPresenter = new Presenter("system", "system", "system")
|
||||
private var audioSettingsInited = false
|
||||
private var permissionsInited = false
|
||||
private var permissions = new Permissions()
|
||||
private var recording = false
|
||||
private var broadcastingRTMP = false
|
||||
private var muted = false
|
||||
private var meetingEnded = false
|
||||
private var meetingMuted = false
|
||||
private var guestPolicy = GuestPolicy.ASK_MODERATOR
|
||||
private var guestPolicySetBy: String = null
|
||||
|
||||
private var hasLastWebUserLeft = false
|
||||
private var lastWebUserLeftOnTimestamp: Long = 0
|
||||
|
||||
private var voiceRecordingFilename: String = ""
|
||||
private var rtmpBroadcastingUrl: String = ""
|
||||
private var deskShareStarted = false
|
||||
private var desktopShareVideoWidth = 0
|
||||
private var desktopShareVideoHeight = 0
|
||||
|
||||
private var extension = new MeetingExtensionProp
|
||||
|
||||
private val startedOn = MeetingStatus2x.timeNowInSeconds;
|
||||
private var breakoutRoomsStartedOn: Long = 0
|
||||
private var breakoutRoomsdurationInMinutes: Int = 0
|
||||
|
||||
private def setCurrentPresenterInfo(pres: Presenter) {
|
||||
currentPresenter = pres
|
||||
}
|
||||
|
||||
private def getCurrentPresenterInfo(): Presenter = {
|
||||
currentPresenter
|
||||
}
|
||||
|
||||
private def addGlobalAudioConnection(userID: String): Boolean = {
|
||||
globalAudioConnectionCounter.get(userID) match {
|
||||
case Some(vc) => {
|
||||
globalAudioConnectionCounter += userID -> (vc + 1)
|
||||
false
|
||||
}
|
||||
case None => {
|
||||
globalAudioConnectionCounter += userID -> 1
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def removeGlobalAudioConnection(userID: String): Boolean = {
|
||||
globalAudioConnectionCounter.get(userID) match {
|
||||
case Some(vc) => {
|
||||
if (vc == 1) {
|
||||
globalAudioConnectionCounter -= userID
|
||||
true
|
||||
} else {
|
||||
globalAudioConnectionCounter += userID -> (vc - 1)
|
||||
false
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def startRecordingVoice() {
|
||||
recordingVoice = true
|
||||
}
|
||||
|
||||
private def stopRecordingVoice() {
|
||||
recordingVoice = false
|
||||
}
|
||||
|
||||
private def isVoiceRecording: Boolean = {
|
||||
recordingVoice
|
||||
}
|
||||
|
||||
private def resetDesktopSharingParams() = {
|
||||
broadcastingRTMP = false
|
||||
deskShareStarted = false
|
||||
rtmpBroadcastingUrl = ""
|
||||
desktopShareVideoWidth = 0
|
||||
desktopShareVideoHeight = 0
|
||||
}
|
||||
|
||||
private def getDeskShareStarted(): Boolean = {
|
||||
return deskShareStarted
|
||||
}
|
||||
|
||||
private def setDeskShareStarted(b: Boolean) {
|
||||
deskShareStarted = b
|
||||
}
|
||||
|
||||
private def setDesktopShareVideoWidth(videoWidth: Int) {
|
||||
desktopShareVideoWidth = videoWidth
|
||||
}
|
||||
|
||||
private def setDesktopShareVideoHeight(videoHeight: Int) {
|
||||
desktopShareVideoHeight = videoHeight
|
||||
}
|
||||
|
||||
private def getDesktopShareVideoWidth(): Int = {
|
||||
desktopShareVideoWidth
|
||||
}
|
||||
|
||||
private def getDesktopShareVideoHeight(): Int = {
|
||||
desktopShareVideoHeight
|
||||
}
|
||||
|
||||
private def broadcastingRTMPStarted() {
|
||||
broadcastingRTMP = true
|
||||
}
|
||||
|
||||
private def isBroadcastingRTMP(): Boolean = {
|
||||
broadcastingRTMP
|
||||
}
|
||||
|
||||
private def broadcastingRTMPStopped() {
|
||||
broadcastingRTMP = false
|
||||
}
|
||||
|
||||
private def setRTMPBroadcastingUrl(path: String) {
|
||||
rtmpBroadcastingUrl = path
|
||||
}
|
||||
|
||||
private def getRTMPBroadcastingUrl(): String = {
|
||||
rtmpBroadcastingUrl
|
||||
}
|
||||
|
||||
private def isExtensionAllowed(): Boolean = extension.numExtensions < extension.maxExtensions
|
||||
private def incNumExtension(): Int = {
|
||||
if (extension.numExtensions < extension.maxExtensions) {
|
||||
extension = extension.copy(numExtensions = extension.numExtensions + 1); extension.numExtensions
|
||||
}
|
||||
extension.numExtensions
|
||||
}
|
||||
|
||||
private def notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
|
||||
private def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
|
||||
private def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
|
||||
|
||||
private def getMeetingExtensionProp(): MeetingExtensionProp = extension
|
||||
private def muteMeeting() = meetingMuted = true
|
||||
private def unmuteMeeting() = meetingMuted = false
|
||||
private def isMeetingMuted(): Boolean = meetingMuted
|
||||
private def recordingStarted() = recording = true
|
||||
private def recordingStopped() = recording = false
|
||||
private def isRecording(): Boolean = recording
|
||||
private def lastWebUserLeft() = lastWebUserLeftOnTimestamp = MeetingStatus2x.timeNowInMinutes
|
||||
private def lastWebUserLeftOn(): Long = lastWebUserLeftOnTimestamp
|
||||
private def resetLastWebUserLeftOn() = lastWebUserLeftOnTimestamp = 0
|
||||
private def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path
|
||||
private def getVoiceRecordingFilename(): String = voiceRecordingFilename
|
||||
private def permisionsInitialized(): Boolean = permissionsInited
|
||||
private def initializePermissions() = permissionsInited = true
|
||||
private def audioSettingsInitialized(): Boolean = audioSettingsInited
|
||||
private def initializeAudioSettings() = audioSettingsInited = true
|
||||
private def permissionsEqual(other: Permissions): Boolean = permissions == other
|
||||
private def lockLayout(lock: Boolean) = permissions = permissions.copy(lockedLayout = lock)
|
||||
private def getPermissions(): Permissions = permissions
|
||||
private def setPermissions(p: Permissions) = permissions = p
|
||||
private def meetingHasEnded() = meetingEnded = true
|
||||
private def hasMeetingEnded(): Boolean = meetingEnded
|
||||
|
||||
private def getGuestPolicy(): GuestPolicy.GuestPolicy = guestPolicy
|
||||
private def setGuestPolicy(policy: GuestPolicy.GuestPolicy) = guestPolicy = policy
|
||||
private def getGuestPolicySetBy(): String = guestPolicySetBy
|
||||
private def setGuestPolicySetBy(user: String) = guestPolicySetBy = user
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.bigbluebutton.core2
|
||||
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common2.messages._
|
||||
import org.bigbluebutton.core.bus.{ BbbMsgEvent, BbbMsgRouterEventBus }
|
||||
|
||||
trait ReceivedMessageRouter extends SystemConfiguration {
|
||||
val eventBus: BbbMsgRouterEventBus
|
||||
|
||||
def publish(msg: BbbMsgEvent): Unit = {
|
||||
eventBus.publish(msg)
|
||||
}
|
||||
|
||||
def send(envelope: BbbCoreEnvelope, msg: CreateMeetingReqMsg): Unit = {
|
||||
val event = BbbMsgEvent(meetingManagerChannel, BbbCommonEnvCoreMsg(envelope, msg))
|
||||
publish(event)
|
||||
}
|
||||
|
||||
def send(envelope: BbbCoreEnvelope, msg: ValidateAuthTokenReqMsg): Unit = {
|
||||
val event = BbbMsgEvent(msg.header.meetingId, BbbCommonEnvCoreMsg(envelope, msg))
|
||||
publish(event)
|
||||
}
|
||||
|
||||
def send(envelope: BbbCoreEnvelope, msg: RegisterUserReqMsg): Unit = {
|
||||
val event = BbbMsgEvent(meetingManagerChannel, BbbCommonEnvCoreMsg(envelope, msg))
|
||||
publish(event)
|
||||
}
|
||||
}
|
48
akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/RunningMeetings.scala
Executable file
48
akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/RunningMeetings.scala
Executable file
@ -0,0 +1,48 @@
|
||||
package org.bigbluebutton.core2
|
||||
|
||||
import org.bigbluebutton.core.running.RunningMeeting
|
||||
|
||||
object RunningMeetings {
|
||||
def findWithId(meetings: RunningMeetings, id: String): Option[RunningMeeting] = {
|
||||
meetings.toVector.find(m => m.props.meetingProp.intId == id)
|
||||
}
|
||||
|
||||
def add(meetings: RunningMeetings, meeting: RunningMeeting): RunningMeeting = {
|
||||
meetings.save(meeting)
|
||||
meeting
|
||||
}
|
||||
|
||||
def remove(meetings: RunningMeetings, id: String): Option[RunningMeeting] = {
|
||||
meetings.remove(id)
|
||||
}
|
||||
|
||||
def numMeetings(meetings: RunningMeetings): Int = {
|
||||
meetings.toVector.length
|
||||
}
|
||||
|
||||
def meetings(meetings: RunningMeetings): Vector[RunningMeeting] = {
|
||||
meetings.toVector
|
||||
}
|
||||
|
||||
def findMeetingWithVoiceConfId(meetings: RunningMeetings, voiceConfId: String): Option[RunningMeeting] = {
|
||||
meetings.toVector.find(m => { m.props.voiceProp.voiceConf == voiceConfId })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RunningMeetings {
|
||||
private var meetings = new collection.immutable.HashMap[String, RunningMeeting]
|
||||
|
||||
private def toVector: Vector[RunningMeeting] = meetings.values.toVector
|
||||
|
||||
private def save(meeting: RunningMeeting): RunningMeeting = {
|
||||
meetings += meeting.props.meetingProp.intId -> meeting
|
||||
meeting
|
||||
}
|
||||
|
||||
private def remove(id: String): Option[RunningMeeting] = {
|
||||
val meeting = meetings.get(id)
|
||||
meeting foreach (u => meetings -= id)
|
||||
meeting
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.bigbluebutton.core2.message.handlers
|
||||
|
||||
trait CreateMeetingReqMsgHdlr {
|
||||
|
||||
}
|
@ -5,32 +5,33 @@ import akka.actor.OneForOneStrategy
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
import redis.actors.RedisSubscriberActor
|
||||
import redis.api.pubsub.{ PMessage, Message }
|
||||
import redis.api.pubsub.{ Message, PMessage }
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.actorRef2Scala
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.core.bus.{ IncomingJsonMessage, IncomingJsonMessageBus, ReceivedJsonMessage }
|
||||
import org.bigbluebutton.core.pubsub.receivers.RedisMessageReceiver
|
||||
import redis.api.servers.ClientSetname
|
||||
|
||||
object AppsRedisSubscriberActor extends SystemConfiguration {
|
||||
|
||||
val channels = Seq("time")
|
||||
val TO_AKKA_APPS = "bbb:to-akka-apps"
|
||||
val channels = Seq("time", toAkkaAppsRedisChannel)
|
||||
val patterns = Seq("bigbluebutton:to-bbb-apps:*", "bigbluebutton:from-voice-conf:*")
|
||||
|
||||
def props(msgReceiver: RedisMessageReceiver): Props =
|
||||
Props(classOf[AppsRedisSubscriberActor], msgReceiver,
|
||||
def props(msgReceiver: RedisMessageReceiver, jsonMsgBus: IncomingJsonMessageBus): Props =
|
||||
Props(classOf[AppsRedisSubscriberActor], msgReceiver, jsonMsgBus,
|
||||
redisHost, redisPort,
|
||||
channels, patterns).withDispatcher("akka.rediscala-subscriber-worker-dispatcher")
|
||||
}
|
||||
|
||||
class AppsRedisSubscriberActor(msgReceiver: RedisMessageReceiver, redisHost: String,
|
||||
class AppsRedisSubscriberActor(msgReceiver: RedisMessageReceiver, jsonMsgBus: IncomingJsonMessageBus, redisHost: String,
|
||||
redisPort: Int,
|
||||
channels: Seq[String] = Nil, patterns: Seq[String] = Nil)
|
||||
extends RedisSubscriberActor(
|
||||
new InetSocketAddress(redisHost, redisPort),
|
||||
channels, patterns) {
|
||||
extends RedisSubscriberActor(new InetSocketAddress(redisHost, redisPort),
|
||||
channels, patterns, onConnectStatus = connected => { println(s"connected: $connected") }) with SystemConfiguration {
|
||||
|
||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||
case e: Exception => {
|
||||
@ -47,11 +48,17 @@ class AppsRedisSubscriberActor(msgReceiver: RedisMessageReceiver, redisHost: Str
|
||||
write(ClientSetname("BbbAppsAkkaSub").encodedRequest)
|
||||
|
||||
def onMessage(message: Message) {
|
||||
log.error(s"SHOULD NOT BE RECEIVING: $message")
|
||||
//log.error(s"SHOULD NOT BE RECEIVING: $message")
|
||||
if (message.channel == toAkkaAppsRedisChannel) {
|
||||
val receivedJsonMessage = new ReceivedJsonMessage(message.channel, message.data.utf8String)
|
||||
log.debug(s"RECEIVED:\n [${receivedJsonMessage.channel}] \n ${receivedJsonMessage.data} \n")
|
||||
jsonMsgBus.publish(IncomingJsonMessage(toAkkaAppsJsonChannel, receivedJsonMessage))
|
||||
}
|
||||
}
|
||||
|
||||
def onPMessage(pmessage: PMessage) {
|
||||
//log.debug(s"RECEIVED:\n $pmessage \n")
|
||||
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data)
|
||||
//log.debug(s"RECEIVED:\n ${pmessage.data.utf8String} \n")
|
||||
|
||||
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data.utf8String)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.bigbluebutton.endpoint.redis
|
||||
|
||||
import akka.actor.Props
|
||||
import redis.RedisClient
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import akka.actor.ActorSystem
|
||||
@ -14,6 +12,6 @@ class KeepAliveRedisPublisher(val system: ActorSystem, sender: RedisPublisher) e
|
||||
|
||||
system.scheduler.schedule(2 seconds, 5 seconds) {
|
||||
val msg = new BbbAppsIsAliveMessage(startedOn, System.currentTimeMillis())
|
||||
sender.publish("bigbluebutton:from-bbb-apps:keepalive", msg.toJson())
|
||||
// sender.publish("bigbluebutton:from-bbb-apps:keepalive", msg.toJson())
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
package org.bigbluebutton.endpoint.redis
|
||||
|
||||
import akka.actor.Props
|
||||
import redis.RedisClient
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import akka.actor.ActorSystem
|
||||
import scala.concurrent.Await
|
||||
import akka.actor.Actor
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import akka.util.ByteString
|
||||
|
||||
class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
|
||||
|
||||
@ -18,8 +14,8 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
|
||||
redis.clientSetname("BbbAppsAkkaPub")
|
||||
|
||||
def publish(channel: String, data: String) {
|
||||
//println("PUBLISH TO [" + channel + "]: \n [" + data + "]")
|
||||
redis.publish(channel, data)
|
||||
println("PUBLISH TO [" + channel + "]: \n [" + data + "]")
|
||||
redis.publish(channel, ByteString(data))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import org.bigbluebutton.common2.domain._
|
||||
import org.bigbluebutton.core.api.GuestPolicy
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
trait AppsTestFixtures {
|
||||
|
||||
@ -21,16 +25,46 @@ trait AppsTestFixtures {
|
||||
val createTime = System.currentTimeMillis
|
||||
val createDate = "Oct 26, 2015"
|
||||
val isBreakout = false
|
||||
val red5DeskShareIP = "127.0.0.1"
|
||||
val red5DeskShareApp = "red5App"
|
||||
val metadata: collection.immutable.Map[String, String] = Map("foo" -> "bar", "bar" -> "baz", "baz" -> "foo")
|
||||
val welcomeMsgTemplate = "Welcome message template"
|
||||
val welcomeMsg = "Welcome message"
|
||||
val modOnlyMessage = "Moderator only message"
|
||||
val dialNumber = "613-555-1234"
|
||||
val maxUsers = 25
|
||||
val guestPolicy = "ALWAYS_ASK"
|
||||
|
||||
val red5DeskShareIPTestFixture = "127.0.0.1"
|
||||
val red5DeskShareAppTestFixtures = "red5App"
|
||||
val metadata: collection.immutable.Map[String, String] = Map("foo" -> "bar", "bar" -> "baz", "baz" -> "foo")
|
||||
val screenshareProps = ScreenshareProps("TODO", "TODO", "TODO")
|
||||
val breakoutProps = BreakoutProps(parentMeetingId, sequence, Vector())
|
||||
|
||||
val meetingStatux2x = new MeetingStatus2x
|
||||
val chatModel = new ChatModel()
|
||||
val layoutModel = new LayoutModel()
|
||||
val meetingModel = new MeetingModel()
|
||||
val pollModel = new PollModel()
|
||||
val wbModel = new WhiteboardModel()
|
||||
val presModel = new PresentationModel()
|
||||
val breakoutModel = new BreakoutRoomModel()
|
||||
val captionModel = new CaptionModel()
|
||||
val notesModel = new SharedNotesModel()
|
||||
val users = new Users
|
||||
val registeredUsers = new RegisteredUsers
|
||||
|
||||
val meetingProp = MeetingProp(name = meetingName, extId = externalMeetingId, intId = meetingId,
|
||||
isBreakout = isBreakout.booleanValue())
|
||||
val durationProps = DurationProps(duration = durationInMinutes, createdTime = createTime, createdDate = createDate)
|
||||
val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword)
|
||||
val recordProp = RecordProp(record = record, autoStartRecording = autoStartRecording,
|
||||
allowStartStopRecording = allowStartStopRecording)
|
||||
val welcomeProp = WelcomeProp(welcomeMsgTemplate = welcomeMsgTemplate, welcomeMsg = welcomeMsg,
|
||||
modOnlyMessage = modOnlyMessage)
|
||||
val voiceProp = VoiceProp(telVoice = voiceConfId, voiceConf = voiceConfId, dialNumber = dialNumber)
|
||||
val usersProp = UsersProp(maxUsers = maxUsers, webcamsOnlyForModerator = webcamsOnlyForModerator,
|
||||
guestPolicy = guestPolicy)
|
||||
val metadataProp = new MetadataProp(metadata)
|
||||
|
||||
val defaultProps = DefaultProps(meetingProp, breakoutProps, durationProps, password, recordProp, welcomeProp, voiceProp,
|
||||
usersProp, metadataProp, screenshareProps)
|
||||
|
||||
val mProps = new MeetingProperties(meetingId, externalMeetingId, parentMeetingId,
|
||||
meetingName, record,
|
||||
voiceConfId, deskshareConfId,
|
||||
durationInMinutes,
|
||||
autoStartRecording, allowStartStopRecording, webcamsOnlyForModerator,
|
||||
moderatorPassword, viewerPassword,
|
||||
createTime, createDate, red5DeskShareIP, red5DeskShareApp,
|
||||
isBreakout, sequence, metadata, GuestPolicy.ALWAYS_ACCEPT)
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import akka.testkit.ImplicitSender
|
||||
import akka.testkit.TestKit
|
||||
import scala.concurrent.duration._
|
||||
import scala.collection.immutable
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.scalatest.WordSpecLike
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.Matchers
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core
|
||||
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.Matchers
|
||||
|
||||
class UnitSpec extends FlatSpec with Matchers with BeforeAndAfterAll
|
@ -1,23 +0,0 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import collection.mutable.Stack
|
||||
import org.scalatest._
|
||||
import org.bigbluebutton.core.UnitSpec
|
||||
|
||||
class BreakoutRoomModelSpec extends UnitSpec {
|
||||
|
||||
"A Stack" should "pop values in last-in-first-out order" in {
|
||||
val stack = new Stack[Int]
|
||||
stack.push(1)
|
||||
stack.push(2)
|
||||
assert(stack.pop() === 2)
|
||||
assert(stack.pop() === 1)
|
||||
}
|
||||
|
||||
it should "throw NoSuchElementException if an empty stack is popped" in {
|
||||
val emptyStack = new Stack[String]
|
||||
intercept[NoSuchElementException] {
|
||||
emptyStack.pop()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import collection.mutable.Stack
|
||||
import org.scalatest._
|
||||
import org.bigbluebutton.core.UnitSpec
|
||||
|
||||
class BreakoutRoomsUtilSpec extends UnitSpec {
|
||||
|
@ -1,17 +1,23 @@
|
||||
package org.bigbluebutton.core.models
|
||||
|
||||
import org.scalatest._
|
||||
import org.bigbluebutton.core.UnitSpec
|
||||
import scala.collection.mutable.Stack
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.{ AppsTestFixtures, UnitSpec }
|
||||
import scala.collection.immutable.List
|
||||
|
||||
class ChatModelTest extends UnitSpec {
|
||||
class ChatModelTest extends UnitSpec with AppsTestFixtures {
|
||||
|
||||
val liveMeeting = new LiveMeeting(defaultProps, meetingStatux2x,
|
||||
chatModel, layoutModel, users, registeredUsers, pollModel,
|
||||
wbModel, presModel, breakoutModel, captionModel, notesModel)
|
||||
|
||||
"A Stack" should "pop values in last-in-first-out order" in {
|
||||
val stack = new Stack[Int]
|
||||
stack.push(1)
|
||||
stack.push(2)
|
||||
assert(stack.pop() === 2)
|
||||
assert(stack.pop() === 1)
|
||||
var stack = List[Int]()
|
||||
stack = stack :+ 1
|
||||
stack = stack :+ 2
|
||||
assert(stack.length === 2)
|
||||
stack = stack.dropRight(1)
|
||||
assert(stack.length === 1)
|
||||
assert(stack.contains(1))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,21 +13,27 @@ class DirectChatModelTest extends UnitSpec {
|
||||
val between = Set("user1", "user2")
|
||||
val directChats = new DirectChats()
|
||||
val dc = DirectChats.create(between, directChats)
|
||||
val dm2 = dc.append(dm)
|
||||
|
||||
// Add a message and check that there is only one message
|
||||
dc.append(dm)
|
||||
assert(dc.messages.length == 1)
|
||||
|
||||
val dm3 = dc.append(dm)
|
||||
// Add another message and check that there are two messages
|
||||
dc.append(dm)
|
||||
assert(dc.messages.length == 2)
|
||||
|
||||
// Find the direct chat
|
||||
val dc2 = DirectChats.find(between, directChats)
|
||||
dc2 match {
|
||||
case Some(directChat) => assert(directChat.messages.length == 2)
|
||||
case None => fail("No direct chat found!")
|
||||
}
|
||||
|
||||
val dm4 = dc.append(dm)
|
||||
// Append a third message and make sure there are three messages
|
||||
dc.append(dm)
|
||||
assert(dc.messages.length == 3)
|
||||
|
||||
// Check that we are updating the correct direct chat in the model.
|
||||
val dc3 = DirectChats.find(between, directChats)
|
||||
dc3 match {
|
||||
case Some(directChat) => assert(directChat.messages.length == 3)
|
||||
|
@ -0,0 +1,40 @@
|
||||
package org.bigbluebutton.core.pubsub.sender
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common2.messages.MessageBody.CreateMeetingReqMsgBody
|
||||
import org.bigbluebutton.core.{ AppsTestFixtures, UnitSpec }
|
||||
import org.bigbluebutton.common2.messages._
|
||||
import org.bigbluebutton.common2.util.JsonUtil
|
||||
import org.bigbluebutton.core.bus.{ BbbMsgEvent, BbbMsgRouterEventBus, ReceivedJsonMessage }
|
||||
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgDeserializer
|
||||
import org.bigbluebutton.core2.ReceivedMessageRouter
|
||||
import org.mockito.Mockito._
|
||||
import org.scalatest.mockito.MockitoSugar
|
||||
|
||||
class ReceivedJsonMsgHandlerTraitTests extends UnitSpec
|
||||
with AppsTestFixtures with MockitoSugar with SystemConfiguration {
|
||||
|
||||
class MessageRouter(val eventBus: BbbMsgRouterEventBus) extends ReceivedMessageRouter {
|
||||
|
||||
}
|
||||
|
||||
"It" should "be able to decode envelope and core message" in {
|
||||
val mockEventBus = mock[BbbMsgRouterEventBus]
|
||||
val classUnderTest = new MessageRouter(mockEventBus)
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-web")
|
||||
val envelope = BbbCoreEnvelope(CreateMeetingReqMsg.NAME, routing)
|
||||
val header = BbbCoreBaseHeader(CreateMeetingReqMsg.NAME)
|
||||
val body = CreateMeetingReqMsgBody(defaultProps)
|
||||
val req = CreateMeetingReqMsg(header, body)
|
||||
|
||||
object JsonDeserializer extends Deserializer
|
||||
|
||||
classUnderTest.send(envelope, req)
|
||||
|
||||
// Then verify the class under test used the mock object as expected
|
||||
// The disconnect user shouldn't be called as user has ability to eject another user
|
||||
val event = BbbMsgEvent(meetingManagerChannel, BbbCommonEnvCoreMsg(envelope, req))
|
||||
verify(mockEventBus, times(1)).publish(event)
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.1"
|
||||
|
||||
scalaVersion := "2.11.6"
|
||||
scalaVersion := "2.12.2"
|
||||
|
||||
scalacOptions ++= Seq(
|
||||
"-unchecked",
|
||||
@ -14,7 +14,7 @@ scalacOptions ++= Seq(
|
||||
"-Xlint",
|
||||
"-Ywarn-dead-code",
|
||||
"-language:_",
|
||||
"-target:jvm-1.7",
|
||||
"-target:jvm-1.8",
|
||||
"-encoding", "UTF-8"
|
||||
)
|
||||
|
||||
@ -38,7 +38,7 @@ testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console",
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
|
||||
|
||||
libraryDependencies ++= {
|
||||
val akkaVersion = "2.3.11"
|
||||
val akkaVersion = "2.5.1"
|
||||
Seq(
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
@ -46,13 +46,13 @@ libraryDependencies ++= {
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.3",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"com.github.etaty" % "rediscala_2.12" % "1.8.0",
|
||||
"commons-codec" % "commons-codec" % "1.10",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "1.7.1",
|
||||
"redis.clients" % "jedis" % "2.1.0",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.19-SNAPSHOT",
|
||||
"org.bigbluebutton" % "bbb-common-message_2.12" % "0.0.19-SNAPSHOT",
|
||||
"org.bigbluebutton" % "bbb-fsesl-client" % "0.0.4"
|
||||
)}
|
||||
|
||||
|
@ -1,23 +1,25 @@
|
||||
package org.bigbluebutton.endpoint.redis
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.actor.OneForOneStrategy
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.net.InetSocketAddress
|
||||
import redis.actors.RedisSubscriberActor
|
||||
import redis.api.pubsub.{ PMessage, Message }
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.actorRef2Scala
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration.DurationInt
|
||||
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.freeswitch.pubsub.receivers.RedisMessageReceiver
|
||||
import redis.api.servers.ClientSetname
|
||||
import org.bigbluebutton.common.converters.FromJsonDecoder
|
||||
import org.bigbluebutton.common.messages.PubSubPongMessage
|
||||
import org.bigbluebutton.freeswitch.pubsub.receivers.RedisMessageReceiver
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import akka.actor.OneForOneStrategy
|
||||
import akka.actor.Props
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import redis.actors.RedisSubscriberActor
|
||||
import redis.api.pubsub.Message
|
||||
import redis.api.pubsub.PMessage
|
||||
import redis.api.servers.ClientSetname
|
||||
|
||||
object AppsRedisSubscriberActor extends SystemConfiguration {
|
||||
|
||||
@ -31,9 +33,10 @@ object AppsRedisSubscriberActor extends SystemConfiguration {
|
||||
}
|
||||
|
||||
class AppsRedisSubscriberActor(val system: ActorSystem, msgReceiver: RedisMessageReceiver, redisHost: String,
|
||||
redisPort: Int, channels: Seq[String] = Nil, patterns: Seq[String] = Nil)
|
||||
redisPort: Int,
|
||||
channels: Seq[String] = Nil, patterns: Seq[String] = Nil)
|
||||
extends RedisSubscriberActor(new InetSocketAddress(redisHost, redisPort),
|
||||
channels, patterns) {
|
||||
channels, patterns, onConnectStatus = connected => { println(s"connected: $connected") }) with SystemConfiguration {
|
||||
|
||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||
case e: Exception => {
|
||||
@ -69,7 +72,7 @@ class AppsRedisSubscriberActor(val system: ActorSystem, msgReceiver: RedisMessag
|
||||
def onPMessage(pmessage: PMessage) {
|
||||
// log.debug(s"pattern message received: $pmessage")
|
||||
|
||||
val msg = decoder.decodeMessage(pmessage.data)
|
||||
val msg = decoder.decodeMessage(pmessage.data.utf8String)
|
||||
|
||||
if (msg != null) {
|
||||
msg match {
|
||||
@ -81,7 +84,7 @@ class AppsRedisSubscriberActor(val system: ActorSystem, msgReceiver: RedisMessag
|
||||
case _ => // do nothing
|
||||
}
|
||||
} else {
|
||||
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data)
|
||||
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data.utf8String)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
|
||||
redis.publish("bigbluebutton:to-bbb-apps:system", json)
|
||||
}
|
||||
|
||||
system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage())
|
||||
// system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage())
|
||||
|
||||
def publish(channel: String, data: String) {
|
||||
//println("PUBLISH TO [" + channel + "]: \n [" + data + "]")
|
||||
|
56
bbb-apps-common/.gitignore
vendored
Normal file
56
bbb-apps-common/.gitignore
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
.DS_Store
|
||||
._.DS_Store*
|
||||
.metadata
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.history
|
||||
.worksheet
|
||||
gen
|
||||
**/*.swp
|
||||
**/*~.nib
|
||||
**/build/
|
||||
**/*.pbxuser
|
||||
**/*.perspective
|
||||
**/*.perspectivev3
|
||||
*.xcworkspace
|
||||
*.xcuserdatad
|
||||
**/target
|
||||
target
|
||||
*.iml
|
||||
project/*.ipr
|
||||
project/*.iml
|
||||
project/*.iws
|
||||
project/out
|
||||
project/*/target
|
||||
project/target
|
||||
project/*/bin
|
||||
project/*/build
|
||||
project/*.iml
|
||||
project/*/*.iml
|
||||
project/.idea
|
||||
project/.idea/*
|
||||
.idea
|
||||
.idea/*
|
||||
.idea/**/*
|
||||
.DS_Store
|
||||
project/.DS_Store
|
||||
project/*/.DS_Store
|
||||
tm.out
|
||||
tmlog*.log
|
||||
*.tm*.epoch
|
||||
out/
|
||||
provisioning/.vagrant
|
||||
provisioning/*/.vagrant
|
||||
provisioning/*/*.known
|
||||
/sbt/akka-patterns-store/
|
||||
/daemon/src/build/
|
||||
*.lock
|
||||
log/
|
||||
tmp/
|
||||
build/
|
||||
akka-patterns-store/
|
||||
lib_managed/
|
||||
.cache
|
||||
bin/
|
||||
|
139
bbb-apps-common/build.sbt
Executable file
139
bbb-apps-common/build.sbt
Executable file
@ -0,0 +1,139 @@
|
||||
|
||||
name := "bbb-apps-common"
|
||||
|
||||
organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.1-SNAPSHOT"
|
||||
|
||||
scalaVersion := "2.12.2"
|
||||
|
||||
scalacOptions ++= Seq(
|
||||
"-unchecked",
|
||||
"-deprecation",
|
||||
"-Xlint",
|
||||
"-Ywarn-dead-code",
|
||||
"-language:_",
|
||||
"-target:jvm-1.8",
|
||||
"-encoding", "UTF-8"
|
||||
)
|
||||
|
||||
// We want to have our jar files in lib_managed dir.
|
||||
// This way we'll have the right path when we import
|
||||
// into eclipse.
|
||||
retrieveManaged := true
|
||||
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console", "junitxml")
|
||||
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
|
||||
|
||||
val scalaV = "2.12.2"
|
||||
val akkaVersion = "2.5.1"
|
||||
val scalaTestV = "2.2.6"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.scala-lang/scala-library
|
||||
libraryDependencies += "org.scala-lang" % "scala-library" % "2.12.2"
|
||||
// https://mvnrepository.com/artifact/org.scala-lang/scala-compiler
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.12.2"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor_2.12
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-actor_2.12" % "2.5.1"
|
||||
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-slf4j_2.12
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-slf4j_2.12" % "2.5.1"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.github.etaty/rediscala_2.12
|
||||
libraryDependencies += "com.github.etaty" % "rediscala_2.12" % "1.8.0"
|
||||
|
||||
libraryDependencies += "com.softwaremill.quicklens" %% "quicklens" % "1.4.8"
|
||||
|
||||
libraryDependencies += "org.bigbluebutton" % "bbb-common-message_2.12" % "0.0.19-SNAPSHOT"
|
||||
|
||||
libraryDependencies += "com.google.code.gson" % "gson" % "2.8.0"
|
||||
libraryDependencies += "redis.clients" % "jedis" % "2.9.0"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
|
||||
libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.5"
|
||||
libraryDependencies += "commons-io" % "commons-io" % "2.4"
|
||||
libraryDependencies += "org.apache.commons" % "commons-pool2" % "2.3"
|
||||
libraryDependencies += "commons-io" % "commons-io" % "2.4"
|
||||
libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.23" % "provided"
|
||||
|
||||
|
||||
libraryDependencies += "junit" % "junit" % "4.12" % "test"
|
||||
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
|
||||
|
||||
// For generating test reports
|
||||
libraryDependencies += "org.pegdown" % "pegdown" % "1.6.0" % "test"
|
||||
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-testkit_2.12
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-testkit_2.12" % "2.5.1" % "test"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.scalactic/scalactic_2.12
|
||||
libraryDependencies += "org.scalactic" % "scalactic_2.12" % "3.0.3" % "test"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.scalatest/scalatest_2.12
|
||||
libraryDependencies += "org.scalatest" % "scalatest_2.12" % "3.0.3" % "test"
|
||||
|
||||
libraryDependencies += "org.mockito" % "mockito-core" % "2.7.22" % "test"
|
||||
|
||||
seq(Revolver.settings: _*)
|
||||
|
||||
//-----------
|
||||
// Packaging
|
||||
//
|
||||
// Reference:
|
||||
// http://xerial.org/blog/2014/03/24/sbt/
|
||||
// http://www.scala-sbt.org/sbt-pgp/usage.html
|
||||
// http://www.scala-sbt.org/0.13/docs/Using-Sonatype.html
|
||||
// http://central.sonatype.org/pages/requirements.html
|
||||
// http://central.sonatype.org/pages/releasing-the-deployment.html
|
||||
//-----------
|
||||
|
||||
// Build pure Java lib (i.e. without scala)
|
||||
// Do not append Scala versions to the generated artifacts
|
||||
//crossPaths := false
|
||||
|
||||
// This forbids including Scala related libraries into the dependency
|
||||
//autoScalaLibrary := false
|
||||
|
||||
/***************************
|
||||
* When developing, change the version above to x.x.x-SNAPSHOT then use the file resolver to
|
||||
* publish to the local maven repo using "sbt publish"
|
||||
*/
|
||||
// Uncomment this to publish to local maven repo while commenting out the nexus repo
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
|
||||
|
||||
|
||||
// Comment this out when publishing to local maven repo using SNAPSHOT version.
|
||||
// To push to sonatype "sbt publishSigned"
|
||||
//publishTo := {
|
||||
// val nexus = "https://oss.sonatype.org/"
|
||||
// if (isSnapshot.value)
|
||||
// Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
// else
|
||||
// Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
//}
|
||||
|
||||
// Enables publishing to maven repo
|
||||
publishMavenStyle := true
|
||||
|
||||
publishArtifact in Test := false
|
||||
|
||||
pomIncludeRepository := { _ => false }
|
||||
|
||||
pomExtra := (
|
||||
<scm>
|
||||
<url>git@github.com:bigbluebutton/bigbluebutton.git</url>
|
||||
<connection>scm:git:git@github.com:bigbluebutton/bigbluebutton.git</connection>
|
||||
</scm>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>ritzalam</id>
|
||||
<name>Richard Alam</name>
|
||||
<url>http://www.bigbluebutton.org</url>
|
||||
</developer>
|
||||
</developers>)
|
||||
|
||||
licenses := Seq("LGPL-3.0" -> url("http://opensource.org/licenses/LGPL-3.0"))
|
||||
|
||||
homepage := Some(url("http://www.bigbluebutton.org"))
|
||||
|
||||
|
0
bbb-apps-common/project/Build.scala
Executable file
0
bbb-apps-common/project/Build.scala
Executable file
1
bbb-apps-common/project/build.properties
Executable file
1
bbb-apps-common/project/build.properties
Executable file
@ -0,0 +1 @@
|
||||
sbt.version=0.13.8
|
9
bbb-apps-common/project/plugins.sbt
Executable file
9
bbb-apps-common/project/plugins.sbt
Executable file
@ -0,0 +1,9 @@
|
||||
addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2")
|
||||
|
||||
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0")
|
||||
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
|
||||
|
||||
//addSbtPlugin("com.artima.supersafe" % "sbtplugin" % "1.1.2")
|
||||
|
||||
|
20
bbb-apps-common/src/main/java/org/bigbluebutton/client/ConnInfo.java
Executable file
20
bbb-apps-common/src/main/java/org/bigbluebutton/client/ConnInfo.java
Executable file
@ -0,0 +1,20 @@
|
||||
package org.bigbluebutton.client;
|
||||
|
||||
|
||||
public class ConnInfo {
|
||||
public final String meetingId;
|
||||
public final String userId;
|
||||
public final String token;
|
||||
public final String connId;
|
||||
public final String sessionId;
|
||||
|
||||
public ConnInfo(String meetingId, String userId, String token,
|
||||
String connId, String sessionId) {
|
||||
this.meetingId = meetingId;
|
||||
this.userId = userId;
|
||||
this.token = token;
|
||||
this.connId = connId;
|
||||
this.sessionId = sessionId;
|
||||
|
||||
}
|
||||
}
|
10
bbb-apps-common/src/main/java/org/bigbluebutton/client/IClientInGW.java
Executable file
10
bbb-apps-common/src/main/java/org/bigbluebutton/client/IClientInGW.java
Executable file
@ -0,0 +1,10 @@
|
||||
package org.bigbluebutton.client;
|
||||
|
||||
|
||||
public interface IClientInGW {
|
||||
void connect(ConnInfo connInfo);
|
||||
void disconnect(ConnInfo connInfo);
|
||||
void handleMsgFromClient(ConnInfo connInfo, String json);
|
||||
void send(String channel, String json);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import org.bigbluebutton.common.messages.EditCaptionHistoryMessage;
|
||||
import org.bigbluebutton.common.messages.SendCaptionHistoryReplyMessage;
|
||||
import org.bigbluebutton.common.messages.UpdateCaptionOwnerMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -16,9 +16,9 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class CaptionClientMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public CaptionClientMessageSender(ConnectionInvokerService service) {
|
||||
public CaptionClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -1,25 +1,24 @@
|
||||
package org.bigbluebutton.red5.client;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bigbluebutton.common.messages.GetChatHistoryReplyMessage;
|
||||
import org.bigbluebutton.common.messages.SendPrivateChatMessage;
|
||||
import org.bigbluebutton.common.messages.SendPublicChatMessage;
|
||||
import org.bigbluebutton.common.messages.ClearPublicChatHistoryReplyMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
import org.bigbluebutton.red5.service.ChatKeyUtil;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.bigbluebutton.common.messages.ClearPublicChatHistoryReplyMessage;
|
||||
import org.bigbluebutton.common.messages.GetChatHistoryReplyMessage;
|
||||
import org.bigbluebutton.common.messages.SendPrivateChatMessage;
|
||||
import org.bigbluebutton.common.messages.SendPublicChatMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
import org.bigbluebutton.red5.client.ChatKeyUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChatClientMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public ChatClientMessageSender(ConnectionInvokerService service) {
|
||||
public ChatClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
13
bbb-apps-common/src/main/java/org/bigbluebutton/red5/client/ChatKeyUtil.java
Executable file
13
bbb-apps-common/src/main/java/org/bigbluebutton/red5/client/ChatKeyUtil.java
Executable file
@ -0,0 +1,13 @@
|
||||
package org.bigbluebutton.red5.client;
|
||||
|
||||
public class ChatKeyUtil {
|
||||
public static final String CHAT_TYPE = "chatType";
|
||||
public static final String FROM_USERID = "fromUserID";
|
||||
public static final String FROM_USERNAME = "fromUsername";
|
||||
public static final String FROM_COLOR = "fromColor";
|
||||
public static final String FROM_TIME = "fromTime";
|
||||
public static final String FROM_TZ_OFFSET = "fromTimezoneOffset";
|
||||
public static final String TO_USERID = "toUserID";
|
||||
public static final String TO_USERNAME = "toUsername";
|
||||
public static final String MESSAGE = "message";
|
||||
}
|
@ -6,16 +6,16 @@ import java.util.Map;
|
||||
import org.bigbluebutton.common.messages.DeskShareNotifyASingleViewerEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class DeskShareMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public DeskShareMessageSender(ConnectionInvokerService service) {
|
||||
public DeskShareMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package org.bigbluebutton.red5.client;
|
||||
|
||||
public interface IReceivedOldMessageHandler {
|
||||
void handleMessage(String pattern, String channel, String message);
|
||||
}
|
@ -16,7 +16,7 @@ import org.bigbluebutton.common.messages.MeetingStateMessage;
|
||||
import org.bigbluebutton.common.messages.NewPermissionsSettingMessage;
|
||||
import org.bigbluebutton.common.messages.UserLockedMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.DisconnectAllClientsMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.DisconnectClientMessage;
|
||||
@ -27,9 +27,9 @@ import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class MeetingClientMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public MeetingClientMessageSender(ConnectionInvokerService service) {
|
||||
public MeetingClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -5,16 +5,16 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.bigbluebutton.common.messages.*;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class PollingClientMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public PollingClientMessageSender(ConnectionInvokerService service) {
|
||||
public PollingClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import org.bigbluebutton.common.messages.PresentationPageResizedMessage;
|
||||
import org.bigbluebutton.common.messages.PresentationRemovedMessage;
|
||||
import org.bigbluebutton.common.messages.PresentationSharedMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -23,9 +23,9 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class PresentationClientMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public PresentationClientMessageSender(ConnectionInvokerService service) {
|
||||
public PresentationClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import org.bigbluebutton.common.messages.CreateAdditionalNotesReplyMessage;
|
||||
import org.bigbluebutton.common.messages.DestroyAdditionalNotesReplyMessage;
|
||||
import org.bigbluebutton.common.messages.SharedNotesSyncNoteReplyMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -17,9 +17,9 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class SharedNotesClientMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public SharedNotesClientMessageSender(ConnectionInvokerService service) {
|
||||
public SharedNotesClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -39,9 +39,8 @@ import org.bigbluebutton.messages.BreakoutRoomsTimeRemainingUpdate;
|
||||
import org.bigbluebutton.messages.TimeRemainingUpdate;
|
||||
import org.bigbluebutton.messages.UpdateBreakoutUsers;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -49,11 +48,10 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class UserClientMessageSender {
|
||||
private static Logger log = Red5LoggerFactory.getLogger(UserClientMessageSender.class, "bigbluebutton");
|
||||
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public UserClientMessageSender(ConnectionInvokerService service) {
|
||||
public UserClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@ -319,8 +317,7 @@ public class UserClientMessageSender {
|
||||
Map<String, Object> message = new HashMap<String, Object>();
|
||||
Gson gson = new Gson();
|
||||
message.put("msg", gson.toJson(args));
|
||||
|
||||
log.info("validateAuthTokenReply - " + gson.toJson(args));
|
||||
|
||||
DirectClientMessage m = new DirectClientMessage(msg.meetingId, msg.userId, "validateAuthTokenReply", message);
|
||||
service.sendMessage(m);
|
||||
}
|
||||
@ -333,8 +330,7 @@ public class UserClientMessageSender {
|
||||
Map<String, Object> message = new HashMap<String, Object>();
|
||||
Gson gson = new Gson();
|
||||
message.put("msg", gson.toJson(args));
|
||||
|
||||
log.info("validateAuthTokenTimedOut - " + gson.toJson(args));
|
||||
|
||||
DirectClientMessage m = new DirectClientMessage(msg.meetingId, msg.userId, "validateAuthTokenTimedOut", message);
|
||||
service.sendMessage(m);
|
||||
}
|
||||
@ -360,7 +356,6 @@ public class UserClientMessageSender {
|
||||
message.put("msg", gson.toJson(args));
|
||||
|
||||
String userId = msg.user.get("userId").toString();
|
||||
log.info("joinMeetingReply - " + gson.toJson(args));
|
||||
|
||||
DirectClientMessage jmr = new DirectClientMessage(msg.meetingId, userId, "joinMeetingReply", message);
|
||||
service.sendMessage(jmr);
|
@ -11,7 +11,7 @@ import org.bigbluebutton.common.messages.SendWhiteboardAnnotationReplyMessage;
|
||||
import org.bigbluebutton.common.messages.UndoWhiteboardReplyMessage;
|
||||
import org.bigbluebutton.common.messages.ModifiedWhiteboardAccessMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -19,9 +19,9 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class WhiteboardClientMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
private IConnectionInvokerService service;
|
||||
|
||||
public WhiteboardClientMessageSender(ConnectionInvokerService service) {
|
||||
public WhiteboardClientMessageSender(IConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public class BroadcastToMeetingMsg implements ClientMessage {
|
||||
|
||||
public final String meetingId;
|
||||
public final String messageName;
|
||||
public final String json;
|
||||
|
||||
public BroadcastToMeetingMsg(String meetingId, String messageName, String json) {
|
||||
this.meetingId = meetingId;
|
||||
this.messageName = messageName;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return messageName;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public class DirectToClientMsg implements ClientMessage{
|
||||
public final String meetingId;
|
||||
public final String connId;
|
||||
public final String json;
|
||||
public final String messageName;
|
||||
|
||||
public DirectToClientMsg(String meetingId, String connId, String messageName, String json) {
|
||||
this.meetingId = meetingId;
|
||||
this.connId = connId;
|
||||
this.messageName = messageName;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return messageName;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user