Merge remote-tracking branch 'upstream/master' into close
This commit is contained in:
commit
32a665d2fa
56
bbb-common-web/.gitignore
vendored
Normal file
56
bbb-common-web/.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/
|
||||
|
1
bbb-common-web/README.md
Normal file
1
bbb-common-web/README.md
Normal file
@ -0,0 +1 @@
|
||||
see http://code.google.com/p/bigbluebutton/wiki/DevelopingBBB
|
117
bbb-common-web/build.sbt
Executable file
117
bbb-common-web/build.sbt
Executable file
@ -0,0 +1,117 @@
|
||||
name := "bbb-common-web"
|
||||
|
||||
organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.1-SNAPSHOT"
|
||||
|
||||
scalaVersion := "2.11.7"
|
||||
|
||||
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.11.7"
|
||||
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaV
|
||||
libraryDependencies += "org.scala-lang" % "scala-library" % scalaV
|
||||
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaV
|
||||
|
||||
// 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.freemarker" % "freemarker" % "2.3.23"
|
||||
libraryDependencies += "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.6.3"
|
||||
// https://mvnrepository.com/artifact/org.codehaus.woodstox/woodstox-core-asl
|
||||
libraryDependencies += "org.codehaus.woodstox" % "woodstox-core-asl" % "4.4.1"
|
||||
|
||||
libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.5"
|
||||
|
||||
libraryDependencies += "org.pegdown" % "pegdown" % "1.4.0" % "test"
|
||||
libraryDependencies += "junit" % "junit" % "4.12" % "test"
|
||||
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.mockito/mockito-core
|
||||
libraryDependencies += "org.mockito" % "mockito-core" % "2.7.12" % "test"
|
||||
libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.1" % "test"
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "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-common-web/project/Build.scala
Executable file
0
bbb-common-web/project/Build.scala
Executable file
1
bbb-common-web/project/build.properties
Executable file
1
bbb-common-web/project/build.properties
Executable file
@ -0,0 +1 @@
|
||||
sbt.version=0.13.8
|
9
bbb-common-web/project/plugins.sbt
Executable file
9
bbb-common-web/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.0")
|
||||
|
||||
|
@ -33,7 +33,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bigbluebutton.api.domain.Recording;
|
||||
import org.bigbluebutton.api.domain.RecordingMetadata;
|
||||
import org.bigbluebutton.api.util.RecordingMetadataReaderHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -64,6 +67,38 @@ public class RecordingService {
|
||||
}
|
||||
}
|
||||
|
||||
public List<RecordingMetadata> getRecordingsMetadata(List<String> recordIDs, List<String> states) {
|
||||
List<RecordingMetadata> recs = new ArrayList<RecordingMetadata>();
|
||||
|
||||
Map<String, List<File>> allDirectories = getAllDirectories(states);
|
||||
if (recordIDs.isEmpty()) {
|
||||
for (Map.Entry<String, List<File>> entry : allDirectories.entrySet()) {
|
||||
recordIDs.addAll(getAllRecordingIds(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
for (String recordID : recordIDs) {
|
||||
for (Map.Entry<String, List<File>> entry : allDirectories.entrySet()) {
|
||||
List<File> _recs = getRecordingsForPath(recordID, entry.getValue());
|
||||
Iterator<File> iterator = _recs.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
RecordingMetadata r = getRecordingMetadata(iterator.next());
|
||||
if (r != null) {
|
||||
recs.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return recs;
|
||||
}
|
||||
|
||||
private static RecordingMetadata getRecordingMetadata(File dir) {
|
||||
File file = new File(dir.getPath() + File.separatorChar + "metadata.xml");
|
||||
RecordingMetadata rec = RecordingMetadataReaderHelper.getRecordingMetadata(file);
|
||||
return rec;
|
||||
}
|
||||
|
||||
public List<Recording> getRecordings(List<String> recordIDs, List<String> states) {
|
||||
List<Recording> recs = new ArrayList<Recording>();
|
||||
|
||||
@ -119,6 +154,45 @@ public class RecordingService {
|
||||
return matchesMetadata;
|
||||
}
|
||||
|
||||
public boolean recordingMatchesMetadata(RecordingMetadata recording, Map<String, String> metadataFilters) {
|
||||
boolean matchesMetadata = true;
|
||||
for (Map.Entry<String, String> filter : metadataFilters.entrySet()) {
|
||||
String metadataValue = recording.getMeta().get().get(filter.getKey());
|
||||
if ( metadataValue == null ) {
|
||||
// The recording doesn't have metadata specified
|
||||
matchesMetadata = false;
|
||||
} else {
|
||||
String filterValue = filter.getValue();
|
||||
if( filterValue.charAt(0) == '%' && filterValue.charAt(filterValue.length()-1) == '%' && metadataValue.contains(filterValue.substring(1, filterValue.length()-1)) ){
|
||||
// Filter value embraced by two wild cards
|
||||
// AND the filter value is part of the metadata value
|
||||
} else if( filterValue.charAt(0) == '%' && metadataValue.endsWith(filterValue.substring(1, filterValue.length())) ) {
|
||||
// Filter value starts with a wild cards
|
||||
// AND the filter value ends with the metadata value
|
||||
} else if( filterValue.charAt(filterValue.length()-1) == '%' && metadataValue.startsWith(filterValue.substring(0, filterValue.length()-1)) ) {
|
||||
// Filter value ends with a wild cards
|
||||
// AND the filter value starts with the metadata value
|
||||
} else if( metadataValue.equals(filterValue) ) {
|
||||
// Filter value doesnt have wildcards
|
||||
// AND the filter value is the same as metadata value
|
||||
} else {
|
||||
matchesMetadata = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchesMetadata;
|
||||
}
|
||||
|
||||
|
||||
public List<RecordingMetadata> filterRecordingsByMetadata(List<RecordingMetadata> recordings, Map<String, String> metadataFilters) {
|
||||
List<RecordingMetadata> resultRecordings = new ArrayList<RecordingMetadata>();
|
||||
for (RecordingMetadata entry : recordings) {
|
||||
if (recordingMatchesMetadata(entry, metadataFilters))
|
||||
resultRecordings.add(entry);
|
||||
}
|
||||
return resultRecordings;
|
||||
}
|
||||
|
||||
public Map<String, Recording> filterRecordingsByMetadata(Map<String, Recording> recordings, Map<String, String> metadataFilters) {
|
||||
Map<String, Recording> resultRecordings = new HashMap<String, Recording>();
|
||||
for (Map.Entry<String, Recording> entry : recordings.entrySet()) {
|
||||
@ -189,7 +263,7 @@ public class RecordingService {
|
||||
return rec;
|
||||
}
|
||||
|
||||
private void deleteRecording(String id, String path) {
|
||||
private static void deleteRecording(String id, String path) {
|
||||
String[] format = getPlaybackFormats(path);
|
||||
for (int i = 0; i < format.length; i++) {
|
||||
List<File> recordings = getDirectories(path + File.separatorChar + format[i]);
|
||||
@ -202,12 +276,12 @@ public class RecordingService {
|
||||
}
|
||||
}
|
||||
|
||||
private void createDirectory(File directory) {
|
||||
private static void createDirectory(File directory) {
|
||||
if (!directory.exists())
|
||||
directory.mkdirs();
|
||||
}
|
||||
|
||||
private void deleteDirectory(File directory) {
|
||||
private static void deleteDirectory(File directory) {
|
||||
/**
|
||||
* Go through each directory and check if it's not empty. We need to
|
||||
* delete files inside a directory before a directory can be deleted.
|
||||
@ -224,7 +298,7 @@ public class RecordingService {
|
||||
directory.delete();
|
||||
}
|
||||
|
||||
private List<File> getDirectories(String path) {
|
||||
private static List<File> getDirectories(String path) {
|
||||
List<File> files = new ArrayList<File>();
|
||||
try {
|
||||
DirectoryStream<Path> stream = Files.newDirectoryStream(FileSystems.getDefault().getPath(path));
|
||||
@ -240,7 +314,7 @@ public class RecordingService {
|
||||
return files;
|
||||
}
|
||||
|
||||
private String[] getPlaybackFormats(String path) {
|
||||
private static String[] getPlaybackFormats(String path) {
|
||||
List<File> dirs = getDirectories(path);
|
||||
String[] formats = new String[dirs.size()];
|
||||
|
||||
@ -315,41 +389,97 @@ public class RecordingService {
|
||||
List<File> recordings = getDirectories(path + File.separatorChar + format[i]);
|
||||
for (int f = 0; f < recordings.size(); f++) {
|
||||
if (recordings.get(f).getName().equalsIgnoreCase(recordingId)) {
|
||||
Recording r = getRecordingInfo(recordings.get(f));
|
||||
if (r != null) {
|
||||
File dest;
|
||||
if (state.equals(Recording.STATE_PUBLISHED)) {
|
||||
dest = new File(publishedDir + File.separatorChar + format[i]);
|
||||
} else if (state.equals(Recording.STATE_UNPUBLISHED)) {
|
||||
dest = new File(unpublishedDir + File.separatorChar + format[i]);
|
||||
} else if (state.equals(Recording.STATE_DELETED)) {
|
||||
dest = new File(deletedDir + File.separatorChar + format[i]);
|
||||
} else {
|
||||
log.debug(String.format("State: %s, is not supported", state));
|
||||
return;
|
||||
}
|
||||
if (!dest.exists())
|
||||
dest.mkdirs();
|
||||
boolean moved = recordings.get(f).renameTo(new File(dest, recordings.get(f).getName()));
|
||||
if (moved) {
|
||||
log.debug("Recording successfully moved!");
|
||||
r.setState(state);
|
||||
r.setPublished(state.equals(Recording.STATE_PUBLISHED));
|
||||
if (state.equals(Recording.STATE_DELETED)) {
|
||||
r.setPlaybackFormat(null);
|
||||
deleteRecording(recordingId, deletedDir);
|
||||
}
|
||||
recordingServiceHelper.writeRecordingInfo(dest.getAbsolutePath() + File.separatorChar + recordings.get(f).getName(), r);
|
||||
log.debug(String.format("Recording successfully %s!", state));
|
||||
} else {
|
||||
log.debug("Recording was not moved");
|
||||
}
|
||||
File dest;
|
||||
if (state.equals(Recording.STATE_PUBLISHED)) {
|
||||
dest = new File(publishedDir + File.separatorChar + format[i]);
|
||||
RecordingService.publishRecording(dest, recordingId, recordings.get(f));
|
||||
} else if (state.equals(Recording.STATE_UNPUBLISHED)) {
|
||||
dest = new File(unpublishedDir + File.separatorChar + format[i]);
|
||||
RecordingService.unpublishRecording(dest, recordingId, recordings.get(f));
|
||||
} else if (state.equals(Recording.STATE_DELETED)) {
|
||||
dest = new File(deletedDir + File.separatorChar + format[i]);
|
||||
RecordingService.deleteRecording(dest, recordingId, recordings.get(f));
|
||||
} else {
|
||||
log.debug(String.format("State: %s, is not supported", state));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void publishRecording(File destDir, String recordingId, File recordingDir) {
|
||||
File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath());
|
||||
RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml);
|
||||
if (r != null) {
|
||||
if (!destDir.exists()) destDir.mkdirs();
|
||||
|
||||
try {
|
||||
FileUtils.moveDirectory(recordingDir, new File(destDir.getPath() + File.separatorChar + recordingId));
|
||||
|
||||
r.setState(Recording.STATE_PUBLISHED);
|
||||
r.setPublished(true);
|
||||
|
||||
File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(
|
||||
destDir.getAbsolutePath() + File.separatorChar + recordingId);
|
||||
|
||||
// Process the changes by saving the recording into metadata.xml
|
||||
RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to publish recording : " + recordingId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void unpublishRecording(File destDir, String recordingId, File recordingDir) {
|
||||
File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath());
|
||||
|
||||
RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml);
|
||||
if (r != null) {
|
||||
if (!destDir.exists()) destDir.mkdirs();
|
||||
|
||||
try {
|
||||
FileUtils.moveDirectory(recordingDir, new File(destDir.getPath() + File.separatorChar + recordingId));
|
||||
r.setState(Recording.STATE_UNPUBLISHED);
|
||||
r.setPublished(false);
|
||||
|
||||
File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(
|
||||
destDir.getAbsolutePath() + File.separatorChar + recordingId);
|
||||
|
||||
// Process the changes by saving the recording into metadata.xml
|
||||
RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to unpublish recording : " + recordingId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteRecording(File destDir, String recordingId, File recordingDir) {
|
||||
File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath());
|
||||
|
||||
RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml);
|
||||
if (r != null) {
|
||||
if (!destDir.exists()) destDir.mkdirs();
|
||||
|
||||
try {
|
||||
FileUtils.moveDirectory(recordingDir, new File(destDir.getPath() + File.separatorChar + recordingId));
|
||||
r.setState(Recording.STATE_DELETED);
|
||||
r.setPublished(false);
|
||||
|
||||
File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(
|
||||
destDir.getAbsolutePath() + File.separatorChar + recordingId);
|
||||
|
||||
// Process the changes by saving the recording into metadata.xml
|
||||
RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r);
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to delete recording : " + recordingId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<File> getAllDirectories(String state) {
|
||||
List<File> allDirectories = new ArrayList<File>();
|
||||
|
||||
@ -415,22 +545,8 @@ public class RecordingService {
|
||||
Map<String,File> recsIndexed = indexRecordings(recs);
|
||||
if ( recsIndexed.containsKey(recordID) ) {
|
||||
File recFile = recsIndexed.get(recordID);
|
||||
Recording rec = getRecordingInfo(recFile);
|
||||
if (rec != null) {
|
||||
for (Map.Entry<String,String> meta : metaParams.entrySet()) {
|
||||
if ( !"".equals(meta.getValue()) ) {
|
||||
// As it has a value, if the meta parameter exists update it, otherwise add it
|
||||
rec.updateMetadata(meta.getKey(), meta.getValue());
|
||||
} else {
|
||||
// As it doesn't have a value, if it exists delete it
|
||||
if ( rec.containsMetadata(meta.getKey()) ) {
|
||||
rec.deleteMetadata(meta.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Process the changes by saving the recording into metadata.xml
|
||||
recordingServiceHelper.writeRecordingInfo(recFile.getAbsolutePath(), rec);
|
||||
}
|
||||
File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recFile.getPath());
|
||||
updateRecordingMetadata(metadataXml, metaParams, metadataXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -438,6 +554,27 @@ public class RecordingService {
|
||||
return;
|
||||
}
|
||||
|
||||
public static void updateRecordingMetadata(File srxMetadataXml, Map<String,String> metaParams, File destMetadataXml) {
|
||||
RecordingMetadata rec = RecordingMetadataReaderHelper.getRecordingMetadata(srxMetadataXml);
|
||||
if (rec != null && rec.getMeta() != null) {
|
||||
for (Map.Entry<String,String> meta : metaParams.entrySet()) {
|
||||
if ( !"".equals(meta.getValue()) ) {
|
||||
// As it has a value, if the meta parameter exists update it, otherwise add it
|
||||
rec.getMeta().set(meta.getKey(), meta.getValue());
|
||||
} else {
|
||||
// As it doesn't have a value, if it exists delete it
|
||||
if ( rec.getMeta().containsKey(meta.getKey()) ) {
|
||||
rec.getMeta().remove(meta.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the changes by saving the recording into metadata.xml
|
||||
RecordingMetadataReaderHelper.saveRecordingMetadata(destMetadataXml, rec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Map<String,File> indexRecordings(List<File> recs) {
|
||||
Map<String,File> indexedRecs = new HashMap<String,File>();
|
||||
|
44
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java
Executable file
44
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java
Executable file
@ -0,0 +1,44 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
||||
|
||||
|
||||
public class Breakout {
|
||||
/**
|
||||
* <breakout parentMeetingId="f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374" sequence="2" meetingId="f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780"/>
|
||||
*/
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String parentMeetingId;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private int sequence;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String meetingId;
|
||||
|
||||
public void setParentMeetingId(String parentMeetingId) {
|
||||
this.parentMeetingId = parentMeetingId;
|
||||
}
|
||||
|
||||
public String getParentMeetingId() {
|
||||
return parentMeetingId;
|
||||
}
|
||||
|
||||
public void setSequence(int sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
public int getSequence() {
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public void setMeetingId(String meetingId) {
|
||||
this.meetingId = meetingId;
|
||||
}
|
||||
|
||||
public String getMeetingId() {
|
||||
return meetingId;
|
||||
}
|
||||
}
|
18
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/BreakoutRoom.java
Executable file
18
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/BreakoutRoom.java
Executable file
@ -0,0 +1,18 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
||||
|
||||
|
||||
public class BreakoutRoom {
|
||||
|
||||
@JacksonXmlText
|
||||
private String value;
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
21
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extensions.java
Executable file
21
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extensions.java
Executable file
@ -0,0 +1,21 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
|
||||
/**
|
||||
* Created by ritz on 2017-03-11.
|
||||
*/
|
||||
public class Extensions {
|
||||
@JacksonXmlProperty(localName = "preview")
|
||||
@JacksonXmlElementWrapper(useWrapping = false)
|
||||
private Preview preview;
|
||||
|
||||
public void setPreview(Preview preview) {
|
||||
this.preview = preview;
|
||||
}
|
||||
|
||||
public Preview getPreview() {
|
||||
return preview;
|
||||
}
|
||||
}
|
59
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Image.java
Executable file
59
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Image.java
Executable file
@ -0,0 +1,59 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
||||
|
||||
public class Image {
|
||||
|
||||
/**
|
||||
* <image width="176" height="136" alt="Welcome to">
|
||||
* http://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png
|
||||
* </image>
|
||||
*/
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String width;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String height;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String alt;
|
||||
|
||||
@JacksonXmlText
|
||||
private String value;
|
||||
|
||||
|
||||
public void setWidth(String width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public String getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setHeight(String height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public String getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setAlt(String alt) {
|
||||
this.alt = alt;
|
||||
}
|
||||
|
||||
public String getAlt() {
|
||||
return alt;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
@ -19,20 +19,14 @@
|
||||
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Meeting {
|
||||
private static Logger log = LoggerFactory.getLogger(Meeting.class);
|
||||
|
||||
|
||||
private static final long MILLIS_IN_A_MINUTE = 60000;
|
||||
|
||||
private String name;
|
||||
@ -68,6 +62,7 @@ public class Meeting {
|
||||
private final ConcurrentMap<String, Long> registeredUsers;
|
||||
private final ConcurrentMap<String, Config> configs;
|
||||
private final Boolean isBreakout;
|
||||
private final List<String> breakoutRooms = new ArrayList();
|
||||
|
||||
private long lastUserLeftOn = 0;
|
||||
|
||||
@ -102,6 +97,14 @@ public class Meeting {
|
||||
configs = new ConcurrentHashMap<String, Config>();
|
||||
}
|
||||
|
||||
public void addBreakoutRoom(String meetingId) {
|
||||
breakoutRooms.add(meetingId);
|
||||
}
|
||||
|
||||
public List<String> getBreakoutRooms() {
|
||||
return breakoutRooms;
|
||||
}
|
||||
|
||||
public String storeConfig(boolean defaultConfig, String config) {
|
||||
String token = RandomStringUtils.randomAlphanumeric(8);
|
||||
while (configs.containsKey(token)) {
|
53
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/MeetingInfo.java
Executable file
53
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/MeetingInfo.java
Executable file
@ -0,0 +1,53 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
|
||||
public class MeetingInfo {
|
||||
/**
|
||||
* <meeting id="random-2810069" name="random-2810069" breakout="false"/>
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String id;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String externalId;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String name;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private boolean breakout;
|
||||
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setExternalId(String externalId) {
|
||||
this.externalId = externalId;
|
||||
}
|
||||
|
||||
public String getExternalId() {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setBreakout(boolean breakout) {
|
||||
this.breakout = breakout;
|
||||
}
|
||||
|
||||
public boolean isBreakout() {
|
||||
return breakout;
|
||||
}
|
||||
}
|
31
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java
Executable file
31
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java
Executable file
@ -0,0 +1,31 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@JacksonXmlRootElement(localName = "meta")
|
||||
public class Metadata {
|
||||
private Map<String,String> map = new TreeMap<String,String>();
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, String> get() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void set(String name, String value) {
|
||||
map.put(name, value);
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
map.remove(key);
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
}
|
18
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java
Executable file
18
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java
Executable file
@ -0,0 +1,18 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
|
||||
public class Preview {
|
||||
@JacksonXmlElementWrapper(localName = "images")
|
||||
@JacksonXmlProperty(localName = "image")
|
||||
private Image[] images;
|
||||
|
||||
public void setImages(Image[] images) {
|
||||
this.images = images;
|
||||
}
|
||||
|
||||
public Image[] getImages() {
|
||||
return images;
|
||||
}
|
||||
}
|
150
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java
Executable file
150
bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java
Executable file
@ -0,0 +1,150 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@JacksonXmlRootElement(localName = "recording")
|
||||
public class RecordingMetadata {
|
||||
/**
|
||||
* <recording>
|
||||
<id>32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780</id>
|
||||
<state>published</state>
|
||||
<published>true</published>
|
||||
<start_time>1489173065780</start_time>
|
||||
<end_time>1489173199386</end_time>
|
||||
<breakout parentMeetingId="f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374" sequence="2" meetingId="f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780"/>
|
||||
<meta>
|
||||
<meetingId>f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780</meetingId>
|
||||
<meetingName>random-2810069 (Room - 2)</meetingName>
|
||||
<isBreakout>true</isBreakout>
|
||||
</meta>
|
||||
<playback>
|
||||
<format>presentation</format>
|
||||
<link>http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780</link>
|
||||
<processing_time>9841</processing_time>
|
||||
<duration>126376</duration>
|
||||
<extensions>
|
||||
<preview>
|
||||
<images>
|
||||
<image width="176" height="136" alt="Welcome to">http://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png</image>
|
||||
</images>
|
||||
</preview>
|
||||
</extensions>
|
||||
</playback>
|
||||
</recording>
|
||||
*/
|
||||
|
||||
private String id;
|
||||
private String state;
|
||||
private boolean published;
|
||||
|
||||
@JacksonXmlProperty(localName = "start_time")
|
||||
private String startTime;
|
||||
|
||||
@JacksonXmlProperty(localName = "end_time")
|
||||
private String endTime;
|
||||
|
||||
@JacksonXmlProperty(localName = "participants")
|
||||
private int participants;
|
||||
|
||||
@JacksonXmlProperty(localName = "meeting")
|
||||
private MeetingInfo meetingInfo;
|
||||
|
||||
private Breakout breakout;
|
||||
|
||||
@JacksonXmlElementWrapper(localName = "breakoutRooms")
|
||||
@JacksonXmlProperty(localName = "breakoutRoom")
|
||||
private BreakoutRoom[] breakoutRooms;
|
||||
|
||||
private Metadata meta;
|
||||
|
||||
private RecordingMetadataPlayback playback;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setPublished(boolean published) {
|
||||
this.published = published;
|
||||
}
|
||||
|
||||
public boolean getPublished() {
|
||||
return published;
|
||||
}
|
||||
|
||||
public void setStartTime(String startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public String getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setEndTime(String endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public String getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setParticipants(int participants) {
|
||||
this.participants = participants;
|
||||
}
|
||||
|
||||
public int getParticipants() {
|
||||
return participants;
|
||||
}
|
||||
|
||||
public void setMeeting(MeetingInfo meetingInfo) {
|
||||
this.meetingInfo = meetingInfo;
|
||||
}
|
||||
|
||||
public MeetingInfo getMeeting() {
|
||||
return meetingInfo;
|
||||
}
|
||||
|
||||
public void setBreakout(Breakout breakout) {
|
||||
this.breakout = breakout;
|
||||
}
|
||||
|
||||
public Breakout getBreakout() {
|
||||
return breakout;
|
||||
}
|
||||
|
||||
public void setBreakoutRooms(BreakoutRoom[] breakoutRooms) {
|
||||
this.breakoutRooms = breakoutRooms;
|
||||
}
|
||||
|
||||
public BreakoutRoom[] getBreakoutRooms() {
|
||||
return breakoutRooms;
|
||||
}
|
||||
|
||||
public void setMeta(Metadata meta) {
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
public Metadata getMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
public RecordingMetadataPlayback getPlayback() {
|
||||
return playback;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package org.bigbluebutton.api.domain;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
|
||||
@JacksonXmlRootElement(localName = "playback")
|
||||
public class RecordingMetadataPlayback {
|
||||
private String format;
|
||||
private String link;
|
||||
|
||||
@JacksonXmlProperty(localName = "processing_time")
|
||||
private Long processingTime;
|
||||
|
||||
private Long duration;
|
||||
|
||||
private Extensions extensions;
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setLink(String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
public void setProcessingTime(Long processingTime) {
|
||||
this.processingTime = processingTime;
|
||||
}
|
||||
|
||||
public Long getProcessingTime() {
|
||||
return processingTime;
|
||||
}
|
||||
|
||||
public void setDuration(Long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public Long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setExtensions(Extensions extensions) {
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
public Extensions getExtensions() {
|
||||
return extensions;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.bigbluebutton.api.util;
|
||||
|
||||
|
||||
import org.bigbluebutton.api.domain.Meeting;
|
||||
|
||||
public class MeetingResponseDetail {
|
||||
|
||||
private final String createdOn;
|
||||
private final Meeting meeting;
|
||||
|
||||
public MeetingResponseDetail(String createdOn, Meeting meeting) {
|
||||
this.createdOn = createdOn;
|
||||
this.meeting = meeting;
|
||||
}
|
||||
|
||||
public String getCreatedOn() {
|
||||
return createdOn;
|
||||
}
|
||||
|
||||
public Meeting getMeeting() {
|
||||
return meeting;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package org.bigbluebutton.api.util;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class MeetingsResponse {
|
||||
|
||||
public final Collection<MeetingResponseDetail> meetings;
|
||||
|
||||
public MeetingsResponse(Collection<MeetingResponseDetail> meetings) {
|
||||
this.meetings = meetings;
|
||||
}
|
||||
}
|
26
bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java
Executable file
26
bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java
Executable file
@ -0,0 +1,26 @@
|
||||
package org.bigbluebutton.api.util;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ParamsUtil {
|
||||
private static final Pattern VALID_ID_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9- ]*$");
|
||||
|
||||
public static final String invalidChars = ",";
|
||||
|
||||
public static String stripControlChars(String text) {
|
||||
return text.replaceAll("\\p{Cc}", "");
|
||||
}
|
||||
|
||||
public static boolean isValidMeetingId(String meetingId) {
|
||||
//return VALID_ID_PATTERN.matcher(meetingId).matches();
|
||||
return !containsChar(meetingId, invalidChars);
|
||||
}
|
||||
|
||||
public static boolean containsChar(String text, String chars) {
|
||||
return StringUtils.containsAny(text, chars);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package org.bigbluebutton.api.util;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import org.bigbluebutton.api.domain.RecordingMetadata;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
import javax.xml.stream.*;
|
||||
import java.io.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RecordingMetadataReaderHelper {
|
||||
private static Logger log = LoggerFactory.getLogger(RecordingMetadataReaderHelper.class);
|
||||
|
||||
public static String inputStreamToString(InputStream is) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
br.close();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static RecordingMetadata getRecordingMetadata(File metadataXml) {
|
||||
XMLInputFactory factory = XMLInputFactory.newInstance();
|
||||
|
||||
JacksonXmlModule module = new JacksonXmlModule();
|
||||
// and then configure, for example:
|
||||
module.setDefaultUseWrapper(false);
|
||||
|
||||
XmlMapper mapper = new XmlMapper(module);
|
||||
|
||||
//Reading from xml file and creating XMLStreamReader
|
||||
XMLStreamReader reader = null;
|
||||
RecordingMetadata recMeta = null;
|
||||
try {
|
||||
reader = factory.createXMLStreamReader(new FileInputStream(metadataXml));
|
||||
recMeta = mapper.readValue(reader, RecordingMetadata.class);
|
||||
} catch (XMLStreamException e) {
|
||||
log.error("Failed to read metadata xml for recording: " + metadataXml.getAbsolutePath(), e);
|
||||
} catch (FileNotFoundException e) {
|
||||
log.error("File not found: " + metadataXml.getAbsolutePath(), e);
|
||||
} catch (IOException e) {
|
||||
log.error("IOException on " + metadataXml.getAbsolutePath(), e);
|
||||
}
|
||||
|
||||
return recMeta;
|
||||
}
|
||||
|
||||
public static File getMetadataXmlLocation(String destDir) {
|
||||
return new File(destDir + File.separatorChar + "metadata.xml");
|
||||
}
|
||||
|
||||
public static void saveRecordingMetadata(File metadataXml, RecordingMetadata recordingMetadata) {
|
||||
|
||||
//XMLOutputFactory factory = XMLOutputFactory.newInstance();
|
||||
JacksonXmlModule module = new JacksonXmlModule();
|
||||
module.setDefaultUseWrapper(false);
|
||||
|
||||
XmlMapper mapper = new XmlMapper(module);
|
||||
|
||||
//Reading from xml file and creating XMLStreamReader
|
||||
//XMLStreamWriter writer = null;
|
||||
try {
|
||||
//writer = factory.createXMLStreamWriter(new FileOutputStream(metadataXml));
|
||||
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
mapper.writeValue(metadataXml, recordingMetadata);
|
||||
} catch (FileNotFoundException e) {
|
||||
log.error("File not found: " + metadataXml.getAbsolutePath(), e);
|
||||
} catch (IOException e) {
|
||||
log.error("IOException on " + metadataXml.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
}
|
124
bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java
Executable file
124
bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java
Executable file
@ -0,0 +1,124 @@
|
||||
package org.bigbluebutton.api.util;
|
||||
|
||||
import org.bigbluebutton.api.domain.Meeting;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.*;
|
||||
|
||||
import freemarker.template.*;
|
||||
import org.bigbluebutton.api.domain.RecordingMetadata;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ResponseBuilder {
|
||||
private static Logger log = LoggerFactory.getLogger(ResponseBuilder.class);
|
||||
|
||||
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
|
||||
|
||||
public ResponseBuilder(File templatesLoc) {
|
||||
|
||||
try {
|
||||
cfg.setDirectoryForTemplateLoading(templatesLoc);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
cfg.setDefaultEncoding("UTF-8");
|
||||
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||
cfg.setLogTemplateExceptions(false);
|
||||
}
|
||||
|
||||
public String formatPrettyDate(Long timestamp) {
|
||||
return new Date(timestamp).toString();
|
||||
}
|
||||
|
||||
public String buildGetMeetingInfoResponse(Meeting meeting, String returnCode) {
|
||||
String createdOn = formatPrettyDate(meeting.getCreateTime());
|
||||
|
||||
Template ftl = null;
|
||||
try {
|
||||
ftl = cfg.getTemplate("get-meeting-info.ftl");
|
||||
} catch (IOException e) {
|
||||
log.error("Cannot find get-meeting-info.ftl template for meeting : " + meeting.getInternalId(), e);
|
||||
}
|
||||
|
||||
StringWriter xmlText = new StringWriter();
|
||||
|
||||
Map root = new HashMap();
|
||||
root.put("returnCode", returnCode);
|
||||
root.put("createdOn", createdOn);
|
||||
root.put("meeting", meeting);
|
||||
|
||||
try {
|
||||
ftl.process(root, xmlText);
|
||||
} catch (TemplateException e) {
|
||||
log.error("Template exception for meeting : " + meeting.getInternalId(), e);
|
||||
} catch (IOException e) {
|
||||
log.error("IO exception for meeting : " + meeting.getInternalId(), e);
|
||||
}
|
||||
|
||||
return xmlText.toString();
|
||||
}
|
||||
|
||||
public String buildGetMeetingsResponse(Collection<Meeting> meetings, String returnCode) {
|
||||
|
||||
ArrayList<MeetingResponseDetail> meetingResponseDetails = new ArrayList<MeetingResponseDetail>();
|
||||
|
||||
for (Meeting meeting : meetings) {
|
||||
String createdOn = formatPrettyDate(meeting.getCreateTime());
|
||||
MeetingResponseDetail details = new MeetingResponseDetail(createdOn, meeting);
|
||||
meetingResponseDetails.add(details);
|
||||
}
|
||||
|
||||
Template ftl = null;
|
||||
try {
|
||||
ftl = cfg.getTemplate("get-meetings.ftl");
|
||||
} catch (IOException e) {
|
||||
log.error("IO exception for get-meetings.ftl : ", e);
|
||||
}
|
||||
|
||||
StringWriter xmlText = new StringWriter();
|
||||
|
||||
Map root = new HashMap();
|
||||
root.put("returnCode", returnCode);
|
||||
root.put("meetingDetailsList", meetingResponseDetails);
|
||||
|
||||
try {
|
||||
ftl.process(root, xmlText);
|
||||
} catch (TemplateException e) {
|
||||
log.error("Template exception : ", e);
|
||||
} catch (IOException e) {
|
||||
log.error("IO exception for get-meetings.ftl : ", e);
|
||||
}
|
||||
|
||||
return xmlText.toString();
|
||||
}
|
||||
|
||||
public String buildGetRecordingsResponse(List<RecordingMetadata> recordings, String returnCode) {
|
||||
|
||||
Template ftl = null;
|
||||
try {
|
||||
ftl = cfg.getTemplate("get-recordings.ftl");
|
||||
} catch (IOException e) {
|
||||
log.error("IO exception for get-recordings.ftl : ", e);
|
||||
}
|
||||
|
||||
StringWriter xmlText = new StringWriter();
|
||||
|
||||
Map root = new HashMap();
|
||||
root.put("returnCode", returnCode);
|
||||
root.put("recordings", recordings);
|
||||
|
||||
try {
|
||||
ftl.process(root, xmlText);
|
||||
} catch (TemplateException e) {
|
||||
log.error("Template exception : ", e);
|
||||
} catch (IOException e) {
|
||||
log.error("IO exception for get-meetings.ftl : ", e);
|
||||
}
|
||||
|
||||
return xmlText.toString();
|
||||
}
|
||||
}
|
32
bbb-common-web/src/test/resources/breakout-room-metadata.xml
Executable file
32
bbb-common-web/src/test/resources/breakout-room-metadata.xml
Executable file
@ -0,0 +1,32 @@
|
||||
<recording>
|
||||
<id>32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780</id>
|
||||
<state>published</state>
|
||||
<published>true</published>
|
||||
<start_time>1489173065780</start_time>
|
||||
<end_time>1489173199386</end_time>
|
||||
<participants>10</participants>
|
||||
<meeting id="f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374" externalId="random-2810069" name="random-2810069" breakout="false"/>
|
||||
<breakout parentMeetingId="f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374" sequence="2" meetingId="f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780"/>
|
||||
<breakoutRooms>
|
||||
<breakoutRoom>32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780</breakoutRoom>
|
||||
<breakoutRoom>721d83a3907548734d4a505992ebb94ec1454a91-1489173065780</breakoutRoom>
|
||||
</breakoutRooms>
|
||||
<meta>
|
||||
<meetingId>f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780</meetingId>
|
||||
<meetingName>random-2810069 (Room - 2)</meetingName>
|
||||
<isBreakout>true</isBreakout>
|
||||
</meta>
|
||||
<playback>
|
||||
<format>presentation</format>
|
||||
<link>http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780</link>
|
||||
<processing_time>9841</processing_time>
|
||||
<duration>126376</duration>
|
||||
<extensions>
|
||||
<preview>
|
||||
<images>
|
||||
<image width="176" height="136" alt="Welcome to">http://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png</image>
|
||||
</images>
|
||||
</preview>
|
||||
</extensions>
|
||||
</playback>
|
||||
</recording>
|
78
bbb-common-web/src/test/resources/get-meeting-info.ftl
Executable file
78
bbb-common-web/src/test/resources/get-meeting-info.ftl
Executable file
@ -0,0 +1,78 @@
|
||||
<#-- GET_RECORDINGS FreeMarker XML template -->
|
||||
<response>
|
||||
<#-- Where code is a 'SUCCESS' or 'FAILED' String -->
|
||||
<returncode>${returnCode}</returncode>
|
||||
<meetingName>${meeting.getName()}</meetingName>
|
||||
<meetingID>${meeting.getExternalId()}</meetingID>
|
||||
<internalMeetingID>${meeting.getInternalId()}</internalMeetingID>
|
||||
<createTime>${meeting.getCreateTime()?c}</createTime>
|
||||
<createDate>${createdOn}</createDate>
|
||||
<voiceBridge>${meeting.getTelVoice()}</voiceBridge>
|
||||
<dialNumber>${meeting.getDialNumber()}</dialNumber>
|
||||
<attendeePW>${meeting.getViewerPassword()}</attendeePW>
|
||||
<moderatorPW>${meeting.getModeratorPassword()}</moderatorPW>
|
||||
<running>${meeting.isRunning()?c}</running>
|
||||
<duration>${meeting.getDuration()}</duration>
|
||||
<hasUserJoined>${meeting.hasUserJoined()?c}</hasUserJoined>
|
||||
<recording>${meeting.isRecord()?c}</recording>
|
||||
<hasBeenForciblyEnded>${meeting.isForciblyEnded()?c}</hasBeenForciblyEnded>
|
||||
<startTime>${meeting.getStartTime()?c}</startTime>
|
||||
<endTime>${meeting.getEndTime()}</endTime>
|
||||
<participantCount>${meeting.getNumUsers()}</participantCount>
|
||||
<listenerCount>${meeting.getNumListenOnly()}</listenerCount>
|
||||
<voiceParticipantCount>${meeting.getNumVoiceJoined()}</voiceParticipantCount>
|
||||
<videoCount>${meeting.getNumVideos()}</videoCount>
|
||||
<maxUsers>${meeting.getMaxUsers()}</maxUsers>
|
||||
<moderatorCount>${meeting.getNumModerators()}</moderatorCount>
|
||||
<attendees>
|
||||
<#list meeting.getUsers() as att>
|
||||
<attendee>
|
||||
<userID>${att.getInternalUserId()}</userID>
|
||||
<fullName>${att.getFullname()}</fullName>
|
||||
<role>${att.getRole()}</role>
|
||||
<isPresenter>${att.isPresenter()?c}</isPresenter>
|
||||
<isListeningOnly>${att.isListeningOnly()?c}</isListeningOnly>
|
||||
<hasJoinedVoice>${att.isVoiceJoined()?c}</hasJoinedVoice>
|
||||
<hasVideo>${att.hasVideo()?c}</hasVideo>
|
||||
<#if meeting.getUserCustomData(att.getExternalUserId())??>
|
||||
<#assign ucd = meeting.getUserCustomData(att.getExternalUserId())>
|
||||
<customdata>
|
||||
<#list ucd?keys as prop>
|
||||
<${prop}><![CDATA[${ucd[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</customdata>
|
||||
</#if>
|
||||
</attendee>
|
||||
</#list>
|
||||
</attendees>
|
||||
<#assign m = meeting.getMetadata()>
|
||||
<metadata>
|
||||
<#list m?keys as prop>
|
||||
<${prop}><![CDATA[${m[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</metadata>
|
||||
|
||||
<#if messageKey?has_content>
|
||||
<messageKey>${messageKey}</messageKey>
|
||||
</#if>
|
||||
|
||||
<#if message?has_content>
|
||||
<message>${message}</message>
|
||||
</#if>
|
||||
|
||||
<#if meeting.isBreakout()>
|
||||
<breakout>
|
||||
<parentMeetingID>${meeting.getParentMeetingId()}</parentMeetingID>
|
||||
<sequence>${meeting.getSequence()}</sequence>
|
||||
</breakout>
|
||||
</#if>
|
||||
|
||||
<#list meeting.getBreakoutRooms()>
|
||||
<breakoutRooms>
|
||||
<#items as room>
|
||||
<breakout>${room}</breakout>
|
||||
</#items>
|
||||
</breakoutRooms>
|
||||
</#list>
|
||||
|
||||
</response>
|
78
bbb-common-web/src/test/resources/get-meetings.ftl
Executable file
78
bbb-common-web/src/test/resources/get-meetings.ftl
Executable file
@ -0,0 +1,78 @@
|
||||
<#-- GET_RECORDINGS FreeMarker XML template -->
|
||||
<response>
|
||||
<#-- Where code is a 'SUCCESS' or 'FAILED' String -->
|
||||
<returncode>${returnCode}</returncode>
|
||||
<#list meetingDetailsList>
|
||||
<meetings>
|
||||
<#items as meetingDetail>
|
||||
<#assign meeting = meetingDetail.getMeeting()>
|
||||
<meeting>
|
||||
<meetingName>${meeting.getName()}</meetingName>
|
||||
<meetingID>${meeting.getExternalId()}</meetingID>
|
||||
<internalMeetingID>${meeting.getInternalId()}</internalMeetingID>
|
||||
<createTime>${meeting.getCreateTime()?c}</createTime>
|
||||
<createDate>${meetingDetail.getCreatedOn()}</createDate>
|
||||
<voiceBridge>${meeting.getTelVoice()}</voiceBridge>
|
||||
<dialNumber>${meeting.getDialNumber()}</dialNumber>
|
||||
<attendeePW>${meeting.getViewerPassword()}</attendeePW>
|
||||
<moderatorPW>${meeting.getModeratorPassword()}</moderatorPW>
|
||||
<running>${meeting.isRunning()?c}</running>
|
||||
<duration>${meeting.getDuration()}</duration>
|
||||
<hasUserJoined>${meeting.hasUserJoined()?c}</hasUserJoined>
|
||||
<recording>${meeting.isRecord()?c}</recording>
|
||||
<hasBeenForciblyEnded>${meeting.isForciblyEnded()?c}</hasBeenForciblyEnded>
|
||||
<startTime>${meeting.getStartTime()?c}</startTime>
|
||||
<endTime>${meeting.getEndTime()}</endTime>
|
||||
<participantCount>${meeting.getNumUsers()}</participantCount>
|
||||
<listenerCount>${meeting.getNumListenOnly()}</listenerCount>
|
||||
<voiceParticipantCount>${meeting.getNumVoiceJoined()}</voiceParticipantCount>
|
||||
<videoCount>${meeting.getNumVideos()}</videoCount>
|
||||
<maxUsers>${meeting.getMaxUsers()}</maxUsers>
|
||||
<moderatorCount>${meeting.getNumModerators()}</moderatorCount>
|
||||
<attendees>
|
||||
<#list meetingDetail.meeting.getUsers() as att>
|
||||
<attendee>
|
||||
<userID>${att.getInternalUserId()}</userID>
|
||||
<fullName>${att.getFullname()}</fullName>
|
||||
<role>${att.getRole()}</role>
|
||||
<isPresenter>${att.isPresenter()?c}</isPresenter>
|
||||
<isListeningOnly>${att.isListeningOnly()?c}</isListeningOnly>
|
||||
<hasJoinedVoice>${att.isVoiceJoined()?c}</hasJoinedVoice>
|
||||
<hasVideo>${att.hasVideo()?c}</hasVideo>
|
||||
<#if meeting.getUserCustomData(att.getExternalUserId())??>
|
||||
<#assign ucd = meetingDetail.meeting.getUserCustomData(att.getExternalUserId())>
|
||||
<customdata>
|
||||
<#list ucd?keys as prop>
|
||||
<${prop}><![CDATA[${ucd[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</customdata>
|
||||
</#if>
|
||||
</attendee>
|
||||
</#list>
|
||||
</attendees>
|
||||
<#assign m = meetingDetail.meeting.getMetadata()>
|
||||
<metadata>
|
||||
<#list m?keys as prop>
|
||||
<${prop}><![CDATA[${m[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</metadata>
|
||||
|
||||
<#if meetingDetail.meeting.isBreakout()>
|
||||
<breakout>
|
||||
<parentMeetingID>${meetingDetail.meeting.getParentMeetingId()}</parentMeetingID>
|
||||
<sequence>${meetingDetail.meeting.getSequence()}</sequence>
|
||||
</breakout>
|
||||
</#if>
|
||||
|
||||
<#list meetingDetail.meeting.getBreakoutRooms()>
|
||||
<breakoutRooms>
|
||||
<#items as room>
|
||||
<breakout>${room}</breakout>
|
||||
</#items>
|
||||
</breakoutRooms>
|
||||
</#list>
|
||||
</meeting>
|
||||
</#items>
|
||||
</meetings>
|
||||
</#list>
|
||||
</response>
|
68
bbb-common-web/src/test/resources/get-recordings.ftl
Executable file
68
bbb-common-web/src/test/resources/get-recordings.ftl
Executable file
@ -0,0 +1,68 @@
|
||||
<#-- GET_RECORDINGS FreeMarker XML template -->
|
||||
<#compress>
|
||||
<response>
|
||||
<#-- Where code is a 'SUCCESS' or 'FAILED' String -->
|
||||
<returncode>${returnCode}</returncode>
|
||||
<recordings>
|
||||
<#list recordings as r>
|
||||
<recording>
|
||||
<recordID>${r.getId()}</recordID>
|
||||
<meetingID>${r.getMeeting().getId()?html}</meetingID>
|
||||
<externalMeetingID>${r.getMeeting().getExternalId()?html}</externalMeetingID>
|
||||
<name><![CDATA[${r.getMeeting().getName()}]]></name>
|
||||
<isBreakout>${r.getMeeting().isBreakout()?c}</isBreakout>
|
||||
<published>${r.getPublished()?string}</published>
|
||||
<state>${r.getState()?string}</state>
|
||||
<startTime><#if r.getStartTime()?? && r.getStartTime() != "">${r.getStartTime()}</#if></startTime>
|
||||
<endTime><#if r.getEndTime()?? && r.getEndTime() != "">${r.getEndTime()}</#if></endTime>
|
||||
<participants><#if r.getParticipants()??>${r.getParticipants()}</#if></participants>
|
||||
<#if r.getBreakout()??>
|
||||
<#assign breakout = r.getBreakout()>
|
||||
<breakout>
|
||||
<parentId>${breakout.getParentMeetingId()}</parentId>
|
||||
<sequence>${breakout.getSequence()?c}</sequence>
|
||||
</breakout>
|
||||
</#if>
|
||||
<#if r.getBreakoutRooms()??>
|
||||
<#list r.getBreakoutRooms()>
|
||||
<breakoutRooms>
|
||||
<#items as broom>
|
||||
<breakoutRoom>${broom.getValue()}</breakoutRoom>
|
||||
</#items>
|
||||
</breakoutRooms>
|
||||
</#list>
|
||||
</#if>
|
||||
<#assign m = r.getMeta().get()>
|
||||
<metadata>
|
||||
<#list m?keys as prop>
|
||||
<${prop}><![CDATA[${m[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</metadata>
|
||||
<#assign pb = r.getPlayback()>
|
||||
<playback>
|
||||
<format>${pb.getFormat()}</format>
|
||||
<link>${pb.getLink()}</link>
|
||||
<processingTime>${pb.getProcessingTime()?c}</processingTime>
|
||||
<duration>${pb.getDuration()?c}</duration>
|
||||
<#if pb.getExtensions()??>
|
||||
<extensions>
|
||||
<#if pb.getExtensions().getPreview()??>
|
||||
<#assign prev = pb.getExtensions().getPreview()>
|
||||
<preview>
|
||||
<#list prev.getImages()>
|
||||
<images>
|
||||
<#items as image>
|
||||
<image width="${image.getWidth()}" height="${image.getHeight()}" alt="${image.getAlt()}">${image.getValue()}</image>
|
||||
</#items>
|
||||
</images>
|
||||
</#list>
|
||||
</preview>
|
||||
</#if>
|
||||
</extensions>
|
||||
</#if>
|
||||
</playback>
|
||||
</recording>
|
||||
</#list>
|
||||
</recordings>
|
||||
</response>
|
||||
</#compress>
|
22
bbb-common-web/src/test/resources/meeting-has-breakout-metadata.xml
Executable file
22
bbb-common-web/src/test/resources/meeting-has-breakout-metadata.xml
Executable file
@ -0,0 +1,22 @@
|
||||
<recording>
|
||||
<id>f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374</id>
|
||||
<state>published</state>
|
||||
<published>true</published>
|
||||
<start_time>1489172964374</start_time>
|
||||
<end_time>1489173221647</end_time>
|
||||
<breakoutRooms>
|
||||
<breakoutRoom>32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780</breakoutRoom>
|
||||
<breakoutRoom>721d83a3907548734d4a505992ebb94ec1454a91-1489173065780</breakoutRoom>
|
||||
</breakoutRooms>
|
||||
<meta>
|
||||
<isBreakout>false</isBreakout>
|
||||
<meetingId>random-2810069</meetingId>
|
||||
<meetingName>random-2810069</meetingName>
|
||||
</meta>
|
||||
<playback>
|
||||
<format>presentation</format>
|
||||
<link>http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374</link>
|
||||
<processing_time>87598</processing_time>
|
||||
<duration>226417</duration>
|
||||
</playback>
|
||||
</recording>
|
18
bbb-common-web/src/test/resources/meeting-no-breakout-metadata.xml
Executable file
18
bbb-common-web/src/test/resources/meeting-no-breakout-metadata.xml
Executable file
@ -0,0 +1,18 @@
|
||||
<recording>
|
||||
<id>f3ffe06acedf425565cc024c8ebe89a6552e8782-1489095153417</id>
|
||||
<state>published</state>
|
||||
<published>true</published>
|
||||
<start_time>1489095153417</start_time>
|
||||
<end_time>1489095772224</end_time>
|
||||
<meta>
|
||||
<meetingId>random-2810069</meetingId>
|
||||
<meetingName>random-2810069</meetingName>
|
||||
<isBreakout>false</isBreakout>
|
||||
</meta>
|
||||
<playback>
|
||||
<format>presentation</format>
|
||||
<link>http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489095153417</link>
|
||||
<processing_time>191283</processing_time>
|
||||
<duration>545949</duration>
|
||||
</playback>
|
||||
</recording>
|
7
bbb-common-web/src/test/resources/playback-metadata.xml
Executable file
7
bbb-common-web/src/test/resources/playback-metadata.xml
Executable file
@ -0,0 +1,7 @@
|
||||
<playback>
|
||||
<format>presentation</format>
|
||||
<link>http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489095153417</link>
|
||||
<processing_time>191283</processing_time>
|
||||
<duration>545949</duration>
|
||||
|
||||
</playback>
|
@ -0,0 +1 @@
|
||||
[]
|
@ -0,0 +1,31 @@
|
||||
<recording>
|
||||
<id>f3ffe06acedf425565cc024c8ebe89a6552e8782-1489435015784</id>
|
||||
<state>foo</state>
|
||||
<published>false</published>
|
||||
<start_time>1489435015784</start_time>
|
||||
<end_time>1489435076055</end_time>
|
||||
<meeting id="f3ffe06acedf425565cc024c8ebe89a6552e8782-1489435015784" externalId="random-2810069" name="random-2810069" breakout="false"/>
|
||||
<breakout parentMeetingId="f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374" sequence="2" meetingId="f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780"/>
|
||||
<breakoutRooms>
|
||||
<breakoutRoom>32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780</breakoutRoom>
|
||||
<breakoutRoom>721d83a3907548734d4a505992ebb94ec1454a91-1489173065780</breakoutRoom>
|
||||
</breakoutRooms>
|
||||
<meta>
|
||||
<meetingName>random-2810069</meetingName>
|
||||
<meetingId>random-2810069</meetingId>
|
||||
<isBreakout>false</isBreakout>
|
||||
</meta>
|
||||
<playback>
|
||||
<format>presentation</format>
|
||||
<link>http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489435015784</link>
|
||||
<processing_time>28627</processing_time>
|
||||
<duration>32213</duration>
|
||||
<extensions>
|
||||
<preview>
|
||||
<images>
|
||||
<image width="176" height="136" alt="Welcome to">http://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png</image>
|
||||
</images>
|
||||
</preview>
|
||||
</extensions>
|
||||
</playback>
|
||||
</recording>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<recording id="panzoom_events">
|
||||
<event timestamp="0.0" orig="2440467878.0">
|
||||
<viewBox>0.0 0.0 1600.0 1200.0</viewBox>
|
||||
</event>
|
||||
<event timestamp="19.1" orig="2440504635.0">
|
||||
<viewBox>0.0 0.0 1600.0 1200.0</viewBox>
|
||||
</event>
|
||||
<event timestamp="22.6" orig="2440508157.0">
|
||||
<viewBox>0.0 0.0 1600.0 1200.0</viewBox>
|
||||
</event>
|
||||
</recording>
|
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0"?>
|
||||
<popcorn/>
|
@ -0,0 +1,23 @@
|
||||
package org.bigbluebutton.api.util
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
class ParamsUtilTest extends UnitSpec {
|
||||
|
||||
it should "strip out control chars from text" in {
|
||||
val text = "a\u0000b\u0007c\u008fd"
|
||||
val cleaned = ParamsUtil.stripControlChars(text)
|
||||
assert("abcd" == cleaned)
|
||||
}
|
||||
|
||||
it should "complain about invalid chars in meetingId" in {
|
||||
val meetingId = "Demo , Meeting"
|
||||
assert(ParamsUtil.isValidMeetingId(meetingId) == false)
|
||||
}
|
||||
|
||||
it should "accept valid chars in meetingId" in {
|
||||
val meetingId = "Demo Meeting - 123"
|
||||
assert(ParamsUtil.isValidMeetingId(meetingId) == true)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package org.bigbluebutton.api.util
|
||||
|
||||
import java.io.{File, FileInputStream}
|
||||
import javax.xml.stream.{XMLInputFactory, XMLStreamReader}
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.{JacksonXmlModule, XmlMapper}
|
||||
import org.bigbluebutton.api.domain.{RecordingMetadata, RecordingMetadataPlayback}
|
||||
|
||||
class RecordingMetadataReaderHelperTest extends UnitSpec {
|
||||
|
||||
it should "deserialize playback part of metadata.xml" in {
|
||||
val factory: XMLInputFactory = XMLInputFactory.newInstance();
|
||||
|
||||
val xml = new File("src/test/resources/playback-metadata.xml")
|
||||
val module: JacksonXmlModule = new JacksonXmlModule();
|
||||
// and then configure, for example:
|
||||
module.setDefaultUseWrapper(false);
|
||||
|
||||
val mapper: XmlMapper = new XmlMapper(module)
|
||||
|
||||
//Reading from xml file and creating XMLStreamReader
|
||||
val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(xml))
|
||||
|
||||
val playback: RecordingMetadataPlayback = mapper.readValue(reader, classOf[RecordingMetadataPlayback])
|
||||
|
||||
println("***** FOOO =" + mapper.writeValueAsString(playback))
|
||||
|
||||
assert(playback.getDuration == 545949)
|
||||
|
||||
}
|
||||
|
||||
it should "deserialize metadata.xml" in {
|
||||
val factory: XMLInputFactory = XMLInputFactory.newInstance();
|
||||
|
||||
val xml = new File("src/test/resources/breakout-room-metadata.xml")
|
||||
val module: JacksonXmlModule = new JacksonXmlModule();
|
||||
// and then configure, for example:
|
||||
module.setDefaultUseWrapper(false);
|
||||
|
||||
val mapper: XmlMapper = new XmlMapper(module)
|
||||
|
||||
//Reading from xml file and creating XMLStreamReader
|
||||
val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(xml))
|
||||
|
||||
val recMeta: RecordingMetadata = mapper.readValue(reader, classOf[RecordingMetadata])
|
||||
|
||||
println("***** FOOO =" + mapper.writeValueAsString(recMeta))
|
||||
|
||||
assert(recMeta.getPlayback.getDuration == 126376)
|
||||
|
||||
}
|
||||
|
||||
it should "save metadata.xml" in {
|
||||
val factory: XMLInputFactory = XMLInputFactory.newInstance();
|
||||
|
||||
val xml = new File("src/test/resources/breakout-room-metadata.xml")
|
||||
val module: JacksonXmlModule = new JacksonXmlModule();
|
||||
// and then configure, for example:
|
||||
module.setDefaultUseWrapper(false);
|
||||
|
||||
val mapper: XmlMapper = new XmlMapper(module)
|
||||
|
||||
//Reading from xml file and creating XMLStreamReader
|
||||
val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(xml))
|
||||
|
||||
val recMeta: RecordingMetadata = mapper.readValue(reader, classOf[RecordingMetadata])
|
||||
|
||||
recMeta.getMeta().set("FOO", "BAR");
|
||||
|
||||
val metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation("target")
|
||||
if (metadataXml.exists()) metadataXml.delete()
|
||||
|
||||
RecordingMetadataReaderHelper.saveRecordingMetadata(metadataXml, recMeta)
|
||||
|
||||
assert(metadataXml.exists())
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package org.bigbluebutton.api.util
|
||||
|
||||
import java.io.File
|
||||
import java.util
|
||||
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.bigbluebutton.api.RecordingService
|
||||
|
||||
class RecordingServiceTest extends UnitSpec {
|
||||
|
||||
it should "deserialize playback part of metadata.xml" in {
|
||||
val srcXml = new File("src/test/resources/breakout-room-metadata.xml")
|
||||
val metaParams: util.Map[String, String] = new util.TreeMap[String, String]()
|
||||
|
||||
metaParams.put("foo", "bar")
|
||||
metaParams.put("bar", "baz")
|
||||
|
||||
val destXml = new File("target/updated-metadata.xml")
|
||||
RecordingService.updateRecordingMetadata(srcXml, metaParams, destXml)
|
||||
}
|
||||
|
||||
it should "publish recording" in {
|
||||
// Make a copy of our sample recording
|
||||
val destDir = new File("target/sample-recording/publish")
|
||||
if (destDir.exists()) FileUtils.deleteDirectory(destDir)
|
||||
|
||||
val srcDir = new File("src/test/resources/sample-recording")
|
||||
FileUtils.copyDirectory(srcDir, destDir)
|
||||
|
||||
val recordingId = "foo"
|
||||
val recordingDir = new File(destDir.getPath() + File.separatorChar + recordingId)
|
||||
|
||||
val publishedDir = new File("target/recording/published")
|
||||
RecordingService.publishRecording(publishedDir, recordingId, recordingDir)
|
||||
|
||||
assert(true)
|
||||
}
|
||||
|
||||
it should "unpublish recording" in {
|
||||
// Make a copy of our sample recording
|
||||
val destDir = new File("target/sample-recording/unpublish")
|
||||
if (destDir.exists()) FileUtils.deleteDirectory(destDir)
|
||||
|
||||
val srcDir = new File("src/test/resources/sample-recording")
|
||||
FileUtils.copyDirectory(srcDir, destDir)
|
||||
|
||||
val recordingId = "foo"
|
||||
val recordingDir = new File(destDir.getPath() + File.separatorChar + recordingId)
|
||||
|
||||
val unpublishedDir = new File("target/recording/unpublished")
|
||||
if (unpublishedDir.exists()) FileUtils.deleteDirectory(unpublishedDir)
|
||||
|
||||
RecordingService.unpublishRecording(unpublishedDir, recordingId, recordingDir)
|
||||
|
||||
assert(unpublishedDir.exists())
|
||||
}
|
||||
|
||||
it should "delete recording" in {
|
||||
// Make a copy of our sample recording
|
||||
val destDir = new File("target/sample-recording/delete")
|
||||
if (destDir.exists()) FileUtils.deleteDirectory(destDir)
|
||||
|
||||
val srcDir = new File("src/test/resources/sample-recording")
|
||||
FileUtils.copyDirectory(srcDir, destDir)
|
||||
|
||||
val recordingId = "foo"
|
||||
val recordingDir = new File(destDir.getPath() + File.separatorChar + recordingId)
|
||||
|
||||
val deletedDir = new File("target/recording/deleted")
|
||||
if (deletedDir.exists()) FileUtils.deleteDirectory(deletedDir)
|
||||
|
||||
RecordingService.deleteRecording(deletedDir, recordingId, recordingDir)
|
||||
|
||||
assert(deletedDir.exists())
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
package org.bigbluebutton.api.util
|
||||
|
||||
import java.io.File
|
||||
import java.util
|
||||
|
||||
import org.bigbluebutton.api.domain.{Meeting, RecordingMetadata, User}
|
||||
import org.scalatest._
|
||||
|
||||
class ResponseBuilderTest extends UnitSpec {
|
||||
|
||||
it should "find template" in {
|
||||
val current = new java.io.File( "." ).getCanonicalPath()
|
||||
println("Current dir:"+current)
|
||||
|
||||
|
||||
|
||||
val meetingInfo = new util.TreeMap[String, String]()
|
||||
meetingInfo.put("foo", "foo")
|
||||
meetingInfo.put("bar", "baz")
|
||||
|
||||
val meeting: Meeting = new Meeting.Builder("extMid", "intMid", System.currentTimeMillis())
|
||||
.withName("Demo Meeting").withMaxUsers(25)
|
||||
.withModeratorPass("mp").withViewerPass("ap")
|
||||
.withRecording(true).withDuration(600)
|
||||
.withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115")
|
||||
.withDialNumber("6135551234").withDefaultAvatarURL("/avatar")
|
||||
.withAutoStartRecording(false).withAllowStartStopRecording(true)
|
||||
.withWebcamsOnlyForModerator(false).withMetadata(meetingInfo)
|
||||
.withWelcomeMessageTemplate("hello").withWelcomeMessage("hello")
|
||||
.isBreakout(false).build
|
||||
|
||||
meeting.setParentMeetingId("ParentMeetingId")
|
||||
meeting.setSequence(0);
|
||||
|
||||
meeting.addBreakoutRoom("breakout-room-id-1")
|
||||
meeting.addBreakoutRoom("breakout-room-id-2")
|
||||
meeting.addBreakoutRoom("breakout-room-id-3")
|
||||
|
||||
val user: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo")
|
||||
meeting.userJoined(user)
|
||||
|
||||
val user2: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo")
|
||||
meeting.userJoined(user2)
|
||||
|
||||
val user3: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo")
|
||||
meeting.userJoined(user2)
|
||||
|
||||
val custData = new util.HashMap[String, String]()
|
||||
custData.put("gwapo", "totoo")
|
||||
|
||||
meeting.addUserCustomData("extuid1", custData)
|
||||
|
||||
val templateLoc = new File("src/test/resources")
|
||||
val builder = new ResponseBuilder(templateLoc)
|
||||
def response = builder.buildGetMeetingInfoResponse(meeting, "success")
|
||||
// println(response)
|
||||
|
||||
assert(templateLoc.exists())
|
||||
}
|
||||
|
||||
it should "return meetings" in {
|
||||
val meetingInfo1 = new util.TreeMap[String, String]()
|
||||
meetingInfo1.put("foo", "foo")
|
||||
meetingInfo1.put("bar", "baz")
|
||||
|
||||
val meeting1: Meeting = new Meeting.Builder("extMid1", "intMid1", System.currentTimeMillis())
|
||||
.withName("Demo Meeting 1").withMaxUsers(25)
|
||||
.withModeratorPass("mp").withViewerPass("ap")
|
||||
.withRecording(true).withDuration(600)
|
||||
.withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115")
|
||||
.withDialNumber("6135551234").withDefaultAvatarURL("/avatar")
|
||||
.withAutoStartRecording(false).withAllowStartStopRecording(true)
|
||||
.withWebcamsOnlyForModerator(false).withMetadata(meetingInfo1)
|
||||
.withWelcomeMessageTemplate("hello").withWelcomeMessage("hello")
|
||||
.isBreakout(false).build
|
||||
|
||||
meeting1.setParentMeetingId("ParentMeetingId")
|
||||
meeting1.setSequence(0);
|
||||
|
||||
meeting1.addBreakoutRoom("breakout-room-id-1")
|
||||
meeting1.addBreakoutRoom("breakout-room-id-2")
|
||||
meeting1.addBreakoutRoom("breakout-room-id-3")
|
||||
|
||||
val userm11: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo")
|
||||
meeting1.userJoined(userm11)
|
||||
|
||||
val userm12: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo")
|
||||
meeting1.userJoined(userm12)
|
||||
|
||||
val userm13: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo")
|
||||
meeting1.userJoined(userm13)
|
||||
|
||||
val custDatam1 = new util.HashMap[String, String]()
|
||||
custDatam1.put("gwapo", "totoo")
|
||||
|
||||
meeting1.addUserCustomData("extuid1", custDatam1)
|
||||
|
||||
val meetingInfo2 = new util.TreeMap[String, String]()
|
||||
meetingInfo2.put("foo", "foo")
|
||||
meetingInfo2.put("bar", "baz")
|
||||
|
||||
val meeting2: Meeting = new Meeting.Builder("extMid2", "intMid2", System.currentTimeMillis())
|
||||
.withName("Demo Meeting 2").withMaxUsers(25)
|
||||
.withModeratorPass("mp").withViewerPass("ap")
|
||||
.withRecording(true).withDuration(600)
|
||||
.withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115")
|
||||
.withDialNumber("6135551234").withDefaultAvatarURL("/avatar")
|
||||
.withAutoStartRecording(false).withAllowStartStopRecording(true)
|
||||
.withWebcamsOnlyForModerator(false).withMetadata(meetingInfo2)
|
||||
.withWelcomeMessageTemplate("hello").withWelcomeMessage("hello")
|
||||
.isBreakout(false).build
|
||||
|
||||
meeting2.setParentMeetingId("ParentMeetingId")
|
||||
meeting2.setSequence(0);
|
||||
|
||||
meeting2.addBreakoutRoom("breakout-room-id-1")
|
||||
meeting2.addBreakoutRoom("breakout-room-id-2")
|
||||
meeting2.addBreakoutRoom("breakout-room-id-3")
|
||||
|
||||
val userm21: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo")
|
||||
meeting2.userJoined(userm21)
|
||||
|
||||
val userm22: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo")
|
||||
meeting2.userJoined(userm22)
|
||||
|
||||
val userm23: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo")
|
||||
meeting2.userJoined(userm23)
|
||||
|
||||
val custDatam2 = new util.HashMap[String, String]()
|
||||
custDatam2.put("gwapo", "totoo")
|
||||
|
||||
meeting2.addUserCustomData("extuid1", custDatam2)
|
||||
|
||||
|
||||
val meetingInfo3 = new util.TreeMap[String, String]()
|
||||
meetingInfo3.put("foo", "foo")
|
||||
meetingInfo3.put("bar", "baz")
|
||||
|
||||
val meeting3: Meeting = new Meeting.Builder("extMid", "intMid", System.currentTimeMillis())
|
||||
.withName("Demo Meeting").withMaxUsers(25)
|
||||
.withModeratorPass("mp").withViewerPass("ap")
|
||||
.withRecording(true).withDuration(600)
|
||||
.withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115")
|
||||
.withDialNumber("6135551234").withDefaultAvatarURL("/avatar")
|
||||
.withAutoStartRecording(false).withAllowStartStopRecording(true)
|
||||
.withWebcamsOnlyForModerator(false).withMetadata(meetingInfo3)
|
||||
.withWelcomeMessageTemplate("hello").withWelcomeMessage("hello")
|
||||
.isBreakout(false).build
|
||||
|
||||
meeting3.setParentMeetingId("ParentMeetingId")
|
||||
meeting3.setSequence(0);
|
||||
|
||||
meeting3.addBreakoutRoom("breakout-room-id-1")
|
||||
meeting3.addBreakoutRoom("breakout-room-id-2")
|
||||
meeting3.addBreakoutRoom("breakout-room-id-3")
|
||||
|
||||
val user: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo")
|
||||
meeting3.userJoined(user)
|
||||
|
||||
val user2: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo")
|
||||
meeting3.userJoined(user2)
|
||||
|
||||
val user3: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo")
|
||||
meeting3.userJoined(user2)
|
||||
|
||||
val custData = new util.HashMap[String, String]()
|
||||
custData.put("gwapo", "totoo")
|
||||
|
||||
meeting3.addUserCustomData("extuid1", custData)
|
||||
|
||||
|
||||
|
||||
val meetings = new util.ArrayList[Meeting]()
|
||||
meetings.add(meeting1)
|
||||
meetings.add(meeting2)
|
||||
meetings.add(meeting3)
|
||||
|
||||
val templateLoc = new File("src/test/resources")
|
||||
val builder = new ResponseBuilder(templateLoc)
|
||||
def response = builder.buildGetMeetingsResponse(meetings, "success")
|
||||
// println(response)
|
||||
|
||||
assert(templateLoc.exists())
|
||||
}
|
||||
|
||||
it should "reply to getRecordings api call" in {
|
||||
val templateLoc = new File("src/test/resources")
|
||||
val builder = new ResponseBuilder(templateLoc)
|
||||
|
||||
val metadataXml = new File("src/test/resources/breakout-room-metadata.xml")
|
||||
val recMeta = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml);
|
||||
val recList = new util.ArrayList[RecordingMetadata]()
|
||||
recList.add(recMeta)
|
||||
def response = builder.buildGetRecordingsResponse(recList, "success")
|
||||
println(response)
|
||||
|
||||
assert(templateLoc.exists())
|
||||
}
|
||||
}
|
8
bbb-common-web/src/test/scala/org/bigbluebutton/api/util/UnitSpec.scala
Executable file
8
bbb-common-web/src/test/scala/org/bigbluebutton/api/util/UnitSpec.scala
Executable file
@ -0,0 +1,8 @@
|
||||
package org.bigbluebutton.api.util
|
||||
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.Matchers
|
||||
|
||||
abstract class UnitSpec extends FlatSpec with Matchers with BeforeAndAfterAll
|
@ -16,8 +16,7 @@ dependencies {
|
||||
//redis
|
||||
compile 'redis.clients:jedis:2.7.2'
|
||||
compile 'org.apache.commons:commons-pool2:2.3'
|
||||
|
||||
compile 'commons-lang:commons-lang:2.5'
|
||||
|
||||
compile 'commons-io:commons-io:2.4'
|
||||
compile 'commons-codec:commons-codec:1.10'
|
||||
compile 'com.google.code.gson:gson:1.7.1'
|
||||
@ -26,6 +25,7 @@ dependencies {
|
||||
compile 'com.zaxxer:nuprocess:1.1.0'
|
||||
|
||||
compile 'org.bigbluebutton:bbb-common-message:0.0.18-SNAPSHOT'
|
||||
compile 'org.bigbluebutton:bbb-common-web:0.0.1-SNAPSHOT'
|
||||
|
||||
// Logging
|
||||
// Commenting out as it results in build failure (ralam - may 11, 2014)
|
||||
|
@ -19,6 +19,8 @@
|
||||
package org.bigbluebutton.web.controllers
|
||||
|
||||
import com.google.gson.Gson
|
||||
import org.bigbluebutton.api.domain.RecordingMetadata
|
||||
import org.bigbluebutton.api.util.ResponseBuilder
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
@ -53,9 +55,10 @@ import org.bigbluebutton.web.services.turn.StunTurnService;
|
||||
import org.bigbluebutton.web.services.turn.TurnEntry;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.bigbluebutton.api.util.ResponseBuilder
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.cache.WebappTemplateLoader;
|
||||
import java.io.File;
|
||||
|
||||
class ApiController {
|
||||
private static final Integer SESSION_TIMEOUT = 14400 // 4 hours
|
||||
@ -75,6 +78,8 @@ class ApiController {
|
||||
PresentationUrlDownloadService presDownloadService
|
||||
StunTurnService stunTurnService
|
||||
|
||||
|
||||
|
||||
/* general methods */
|
||||
def index = {
|
||||
log.debug CONTROLLER_NAME + "#index"
|
||||
@ -791,7 +796,15 @@ class ApiController {
|
||||
return;
|
||||
}
|
||||
|
||||
respondWithConferenceDetails(meeting, null, null, null);
|
||||
def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker")
|
||||
ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc))
|
||||
|
||||
def xmlText = responseBuilder.buildGetMeetingInfoResponse(meeting, "success")
|
||||
withFormat {
|
||||
xml {
|
||||
render(text: xmlText, contentType: "text/xml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************
|
||||
@ -850,41 +863,14 @@ class ApiController {
|
||||
}
|
||||
} else {
|
||||
response.addHeader("Cache-Control", "no-cache")
|
||||
|
||||
def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker")
|
||||
ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc))
|
||||
|
||||
def xmlText = responseBuilder.buildGetMeetingsResponse(mtgs, "success")
|
||||
withFormat {
|
||||
xml {
|
||||
render(contentType:"text/xml") {
|
||||
response() {
|
||||
returncode(RESP_CODE_SUCCESS)
|
||||
meetings {
|
||||
for (m in mtgs) {
|
||||
meeting {
|
||||
meetingID() { mkp.yield(m.getExternalId()) }
|
||||
internalMeetingID() { mkp.yield(m.getInternalId()) }
|
||||
if (m.isBreakout()) {
|
||||
parentMeetingID() { mkp.yield(m.getParentMeetingId()) }
|
||||
sequence(m.getSequence())
|
||||
}
|
||||
isBreakout() { mkp.yield(m.isBreakout()) }
|
||||
meetingName() { mkp.yield(m.getName()) }
|
||||
createTime(m.getCreateTime())
|
||||
createDate(formatPrettyDate(m.getCreateTime()))
|
||||
voiceBridge() { mkp.yield(m.getTelVoice()) }
|
||||
dialNumber() { mkp.yield(m.getDialNumber()) }
|
||||
attendeePW() { mkp.yield(m.getViewerPassword()) }
|
||||
moderatorPW() { mkp.yield(m.getModeratorPassword()) }
|
||||
hasBeenForciblyEnded(m.isForciblyEnded() ? "true" : "false")
|
||||
running(m.isRunning() ? "true" : "false")
|
||||
participantCount(m.getNumUsers())
|
||||
listenerCount(m.getNumListenOnly())
|
||||
voiceParticipantCount(m.getNumVoiceJoined())
|
||||
videoCount(m.getNumVideos())
|
||||
duration(m.duration)
|
||||
hasUserJoined(m.hasUserJoined())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
render(text: xmlText, contentType: "text/xml")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1750,6 +1736,8 @@ class ApiController {
|
||||
return
|
||||
}
|
||||
|
||||
log.debug request.getQueryString()
|
||||
|
||||
// Do we agree on the checksum? If not, complain.
|
||||
if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
|
||||
errors.checksumError()
|
||||
@ -1779,8 +1767,12 @@ class ApiController {
|
||||
internalRecordIds = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingIds);
|
||||
}
|
||||
|
||||
Map<String,Recording> recs = meetingService.getRecordings(internalRecordIds, states);
|
||||
recs = meetingService.filterRecordingsByMetadata(recs, ParamsProcessorUtil.processMetaParam(params));
|
||||
for(String intRecId : internalRecordIds){
|
||||
log.debug intRecId
|
||||
}
|
||||
|
||||
List<RecordingMetadata> recsList = meetingService.getRecordingsMetadata(internalRecordIds, states);
|
||||
List<RecordingMetadata> recs = meetingService.filterRecordingsByMetadata(recsList, ParamsProcessorUtil.processMetaParam(params));
|
||||
|
||||
if (recs.isEmpty()) {
|
||||
response.addHeader("Cache-Control", "no-cache")
|
||||
@ -1798,18 +1790,14 @@ class ApiController {
|
||||
}
|
||||
return;
|
||||
}
|
||||
def cfg = new Configuration()
|
||||
|
||||
// Load the XML template
|
||||
// TODO: Maybe there is a better way to define the templates path
|
||||
def wtl = new WebappTemplateLoader(getServletContext(), "/WEB-INF/freemarker")
|
||||
cfg.setTemplateLoader(wtl)
|
||||
def ftl = cfg.getTemplate("get-recordings.ftl")
|
||||
def xmlText = new StringWriter()
|
||||
ftl.process([code:RESP_CODE_SUCCESS, recs:recs.values()], xmlText)
|
||||
def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker")
|
||||
ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc))
|
||||
|
||||
def xmlText = responseBuilder.buildGetRecordingsResponse(recs, "success")
|
||||
withFormat {
|
||||
xml {
|
||||
render(text: xmlText.toString(), contentType: "text/xml")
|
||||
render(text: xmlText, contentType: "text/xml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,10 @@ public class ApiErrors {
|
||||
errors.add(new String[] {"maxParticipantsReached", "The number of participants allowed for this meeting has been reached."});
|
||||
}
|
||||
|
||||
public void addError(String[] error) {
|
||||
errors.add(error);
|
||||
}
|
||||
|
||||
public boolean hasErrors() {
|
||||
return errors.size() > 0;
|
||||
}
|
||||
|
@ -37,11 +37,8 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import org.bigbluebutton.api.domain.Meeting;
|
||||
import org.bigbluebutton.api.domain.Playback;
|
||||
import org.bigbluebutton.api.domain.Recording;
|
||||
import org.bigbluebutton.api.domain.User;
|
||||
import org.bigbluebutton.api.domain.UserSession;
|
||||
|
||||
import org.bigbluebutton.api.domain.*;
|
||||
import org.bigbluebutton.api.messaging.MessageListener;
|
||||
import org.bigbluebutton.api.messaging.MessagingService;
|
||||
import org.bigbluebutton.api.messaging.messages.CreateBreakoutRoom;
|
||||
@ -302,6 +299,14 @@ public class MeetingService implements MessageListener {
|
||||
}
|
||||
|
||||
private void handleCreateMeeting(Meeting m) {
|
||||
if (m.isBreakout()){
|
||||
Meeting parent = meetings.get(m.getParentMeetingId());
|
||||
parent.addBreakoutRoom(m.getExternalId());
|
||||
if (parent.isRecord()) {
|
||||
messagingService.addBreakoutRoom(parent.getInternalId(), m.getInternalId());
|
||||
}
|
||||
}
|
||||
|
||||
if (m.isRecord()) {
|
||||
Map<String, String> metadata = new TreeMap<String, String>();
|
||||
metadata.putAll(m.getMetadata());
|
||||
@ -310,13 +315,15 @@ public class MeetingService implements MessageListener {
|
||||
metadata.put("meetingName", m.getName());
|
||||
metadata.put("isBreakout", m.isBreakout().toString());
|
||||
|
||||
Map<String, String> breakoutMetadata = new TreeMap<String, String>();
|
||||
breakoutMetadata.put("meetingId", m.getExternalId());
|
||||
if (m.isBreakout()){
|
||||
messagingService.recordMeetingInfo(m.getInternalId(), metadata);
|
||||
|
||||
if (m.isBreakout()) {
|
||||
Map<String, String> breakoutMetadata = new TreeMap<String, String>();
|
||||
breakoutMetadata.put("meetingId", m.getExternalId());
|
||||
breakoutMetadata.put("sequence", m.getSequence().toString());
|
||||
breakoutMetadata.put("parentMeetingId", m.getParentMeetingId());
|
||||
messagingService.recordBreakoutInfo(m.getInternalId(), breakoutMetadata);
|
||||
}
|
||||
messagingService.recordMeetingInfo(m.getInternalId(), metadata, breakoutMetadata);
|
||||
}
|
||||
|
||||
Map<String, Object> logData = new HashMap<String, Object>();
|
||||
@ -415,18 +422,30 @@ public class MeetingService implements MessageListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<RecordingMetadata> getRecordingsMetadata(List<String> idList, List<String> states) {
|
||||
List<RecordingMetadata> recsList = recordingService.getRecordingsMetadata(idList, states);
|
||||
return recsList;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Recording> getRecordings(List<String> idList, List<String> states) {
|
||||
List<Recording> recsList = recordingService.getRecordings(idList, states);
|
||||
Map<String, Recording> recs = reorderRecordings(recsList);
|
||||
return recs;
|
||||
}
|
||||
|
||||
public Map<String, Recording> filterRecordingsByMetadata(
|
||||
Map<String, Recording> recordings,
|
||||
Map<String, String> metadataFilters) {
|
||||
public List<RecordingMetadata> filterRecordingsByMetadata(List<RecordingMetadata> recsList,
|
||||
Map<String, String> metadataFilters) {
|
||||
return recordingService.filterRecordingsByMetadata(recsList, metadataFilters);
|
||||
}
|
||||
|
||||
public Map<String, Recording> filterRecordingsByMetadata(Map<String, Recording> recordings,
|
||||
Map<String, String> metadataFilters) {
|
||||
return recordingService.filterRecordingsByMetadata(recordings, metadataFilters);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Map<String, Recording> reorderRecordings(List<Recording> olds) {
|
||||
Map<String, Recording> map = new HashMap<String, Recording>();
|
||||
for (Recording r : olds) {
|
||||
@ -498,8 +517,7 @@ public class MeetingService implements MessageListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateRecordings(List<String> idList,
|
||||
Map<String, String> metaParams) {
|
||||
public void updateRecordings(List<String> idList, Map<String, String> metaParams) {
|
||||
recordingService.updateMetaParams(idList, metaParams);
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bigbluebutton.api.domain.Meeting;
|
||||
import org.bigbluebutton.api.util.ParamsUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.apache.commons.httpclient.*;
|
||||
@ -103,9 +104,14 @@ public class ParamsProcessorUtil {
|
||||
|
||||
// Do we have a meeting id? If not, complain.
|
||||
if(!StringUtils.isEmpty(params.get("meetingID"))) {
|
||||
if (StringUtils.isEmpty(StringUtils.strip(params.get("meetingID")))) {
|
||||
String meetingId = StringUtils.strip(params.get("meetingID"));
|
||||
if (StringUtils.isEmpty(meetingId)) {
|
||||
errors.missingParamError("meetingID");
|
||||
}
|
||||
} else {
|
||||
if (! ParamsUtil.isValidMeetingId(meetingId)) {
|
||||
errors.addError(new String[] {"invalidFormat", "Meeting id contains invalid characters."});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.missingParamError("meetingID");
|
||||
}
|
||||
@ -290,10 +296,14 @@ public class ParamsProcessorUtil {
|
||||
}
|
||||
|
||||
public Meeting processCreateParams(Map<String, String> params) {
|
||||
|
||||
String meetingName = params.get("name");
|
||||
if (meetingName == null) {
|
||||
meetingName = "";
|
||||
}
|
||||
|
||||
meetingName = ParamsUtil.stripControlChars(meetingName);
|
||||
|
||||
String externalMeetingId = params.get("meetingID");
|
||||
|
||||
String viewerPass = processPassword(params.get("attendeePW"));
|
||||
|
@ -27,7 +27,9 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface MessagingService {
|
||||
void recordMeetingInfo(String meetingId, Map<String, String> info, Map<String, String> breakoutInfo);
|
||||
void recordMeetingInfo(String meetingId, Map<String, String> info);
|
||||
void recordBreakoutInfo(String meetingId, Map<String, String> breakoutInfo);
|
||||
void addBreakoutRoom(String parentId, String breakoutId);
|
||||
void destroyMeeting(String meetingID);
|
||||
void createMeeting(String meetingID, String externalMeetingID,
|
||||
String parentMeetingID, String meetingName, Boolean recorded,
|
||||
|
@ -49,8 +49,16 @@ public class RedisMessagingService implements MessagingService {
|
||||
private MessageSender sender;
|
||||
private ToJsonEncoder encoder = new ToJsonEncoder();
|
||||
|
||||
public void recordMeetingInfo(String meetingId, Map<String, String> info, Map<String, String> breakoutInfo) {
|
||||
storeService.recordMeetingInfo(meetingId, info, breakoutInfo);
|
||||
public void recordMeetingInfo(String meetingId, Map<String, String> info) {
|
||||
storeService.recordMeetingInfo(meetingId, info);
|
||||
}
|
||||
|
||||
public void recordBreakoutInfo(String meetingId, Map<String, String> breakoutInfo) {
|
||||
storeService.recordBreakoutInfo(meetingId, breakoutInfo);
|
||||
}
|
||||
|
||||
public void addBreakoutRoom(String parentId, String breakoutId) {
|
||||
storeService.addBreakoutRoom(parentId, breakoutId);
|
||||
}
|
||||
|
||||
public void destroyMeeting(String meetingID) {
|
||||
|
@ -30,8 +30,7 @@ public class RedisStorageService {
|
||||
Protocol.DEFAULT_DATABASE, "BbbRed5AppsPub");
|
||||
}
|
||||
|
||||
public void recordMeetingInfo(String meetingId, Map<String, String> info,
|
||||
Map<String, String> breakoutInfo) {
|
||||
public void recordMeetingInfo(String meetingId, Map<String, String> info) {
|
||||
Jedis jedis = redisPool.getResource();
|
||||
try {
|
||||
for (String key : info.keySet()) {
|
||||
@ -40,20 +39,37 @@ public class RedisStorageService {
|
||||
|
||||
log.debug("Saving metadata in {}", meetingId);
|
||||
jedis.hmset("meeting:info:" + meetingId, info);
|
||||
|
||||
for (String breakoutKey : breakoutInfo.keySet()) {
|
||||
log.debug("Storing breakout metadata {} = {}", breakoutKey,
|
||||
breakoutInfo.get(breakoutKey));
|
||||
}
|
||||
|
||||
log.debug("Saving breakout metadata in {}", meetingId);
|
||||
jedis.hmset("meeting:breakout:" + meetingId, breakoutInfo);
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot record the info meeting:" + meetingId, e);
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void recordBreakoutInfo(String meetingId, Map<String, String> breakoutInfo) {
|
||||
Jedis jedis = redisPool.getResource();
|
||||
try {
|
||||
log.debug("Saving breakout metadata in {}", meetingId);
|
||||
jedis.hmset("meeting:breakout:" + meetingId, breakoutInfo);
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot record the info meeting:" + meetingId, e);
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void addBreakoutRoom(String parentId, String breakoutId) {
|
||||
Jedis jedis = redisPool.getResource();
|
||||
try {
|
||||
|
||||
log.debug("Saving breakout room for meeting {}", parentId);
|
||||
jedis.sadd("meeting:breakout:rooms:" + parentId, breakoutId);
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot record the info meeting:" + parentId, e);
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeMeeting(String meetingId){
|
||||
Jedis jedis = redisPool.getResource();
|
||||
|
78
bigbluebutton-web/web-app/WEB-INF/freemarker/get-meeting-info.ftl
Executable file
78
bigbluebutton-web/web-app/WEB-INF/freemarker/get-meeting-info.ftl
Executable file
@ -0,0 +1,78 @@
|
||||
<#-- GET_RECORDINGS FreeMarker XML template -->
|
||||
<response>
|
||||
<#-- Where code is a 'SUCCESS' or 'FAILED' String -->
|
||||
<returncode>${returnCode}</returncode>
|
||||
<meetingName>${meeting.getName()}</meetingName>
|
||||
<meetingID>${meeting.getExternalId()}</meetingID>
|
||||
<internalMeetingID>${meeting.getInternalId()}</internalMeetingID>
|
||||
<createTime>${meeting.getCreateTime()?c}</createTime>
|
||||
<createDate>${createdOn}</createDate>
|
||||
<voiceBridge>${meeting.getTelVoice()}</voiceBridge>
|
||||
<dialNumber>${meeting.getDialNumber()}</dialNumber>
|
||||
<attendeePW>${meeting.getViewerPassword()}</attendeePW>
|
||||
<moderatorPW>${meeting.getModeratorPassword()}</moderatorPW>
|
||||
<running>${meeting.isRunning()?c}</running>
|
||||
<duration>${meeting.getDuration()}</duration>
|
||||
<hasUserJoined>${meeting.hasUserJoined()?c}</hasUserJoined>
|
||||
<recording>${meeting.isRecord()?c}</recording>
|
||||
<hasBeenForciblyEnded>${meeting.isForciblyEnded()?c}</hasBeenForciblyEnded>
|
||||
<startTime>${meeting.getStartTime()?c}</startTime>
|
||||
<endTime>${meeting.getEndTime()}</endTime>
|
||||
<participantCount>${meeting.getNumUsers()}</participantCount>
|
||||
<listenerCount>${meeting.getNumListenOnly()}</listenerCount>
|
||||
<voiceParticipantCount>${meeting.getNumVoiceJoined()}</voiceParticipantCount>
|
||||
<videoCount>${meeting.getNumVideos()}</videoCount>
|
||||
<maxUsers>${meeting.getMaxUsers()}</maxUsers>
|
||||
<moderatorCount>${meeting.getNumModerators()}</moderatorCount>
|
||||
<attendees>
|
||||
<#list meeting.getUsers() as att>
|
||||
<attendee>
|
||||
<userID>${att.getInternalUserId()}</userID>
|
||||
<fullName>${att.getFullname()}</fullName>
|
||||
<role>${att.getRole()}</role>
|
||||
<isPresenter>${att.isPresenter()?c}</isPresenter>
|
||||
<isListeningOnly>${att.isListeningOnly()?c}</isListeningOnly>
|
||||
<hasJoinedVoice>${att.isVoiceJoined()?c}</hasJoinedVoice>
|
||||
<hasVideo>${att.hasVideo()?c}</hasVideo>
|
||||
<#if meeting.getUserCustomData(att.getExternalUserId())??>
|
||||
<#assign ucd = meeting.getUserCustomData(att.getExternalUserId())>
|
||||
<customdata>
|
||||
<#list ucd?keys as prop>
|
||||
<${prop}><![CDATA[${ucd[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</customdata>
|
||||
</#if>
|
||||
</attendee>
|
||||
</#list>
|
||||
</attendees>
|
||||
<#assign m = meeting.getMetadata()>
|
||||
<metadata>
|
||||
<#list m?keys as prop>
|
||||
<${prop}><![CDATA[${m[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</metadata>
|
||||
|
||||
<#if messageKey?has_content>
|
||||
<messageKey>${messageKey}</messageKey>
|
||||
</#if>
|
||||
|
||||
<#if message?has_content>
|
||||
<message>${message}</message>
|
||||
</#if>
|
||||
|
||||
<#if meeting.isBreakout()>
|
||||
<breakout>
|
||||
<parentMeetingID>${meeting.getParentMeetingId()}</parentMeetingID>
|
||||
<sequence>${meeting.getSequence()}</sequence>
|
||||
</breakout>
|
||||
</#if>
|
||||
|
||||
<#list meeting.getBreakoutRooms()>
|
||||
<breakoutRooms>
|
||||
<#items as room>
|
||||
<breakout>${room}</breakout>
|
||||
</#items>
|
||||
</breakoutRooms>
|
||||
</#list>
|
||||
|
||||
</response>
|
78
bigbluebutton-web/web-app/WEB-INF/freemarker/get-meetings.ftl
Executable file
78
bigbluebutton-web/web-app/WEB-INF/freemarker/get-meetings.ftl
Executable file
@ -0,0 +1,78 @@
|
||||
<#-- GET_RECORDINGS FreeMarker XML template -->
|
||||
<response>
|
||||
<#-- Where code is a 'SUCCESS' or 'FAILED' String -->
|
||||
<returncode>${returnCode}</returncode>
|
||||
<#list meetingDetailsList>
|
||||
<meetings>
|
||||
<#items as meetingDetail>
|
||||
<#assign meeting = meetingDetail.getMeeting()>
|
||||
<meeting>
|
||||
<meetingName>${meeting.getName()}</meetingName>
|
||||
<meetingID>${meeting.getExternalId()}</meetingID>
|
||||
<internalMeetingID>${meeting.getInternalId()}</internalMeetingID>
|
||||
<createTime>${meeting.getCreateTime()?c}</createTime>
|
||||
<createDate>${meetingDetail.getCreatedOn()}</createDate>
|
||||
<voiceBridge>${meeting.getTelVoice()}</voiceBridge>
|
||||
<dialNumber>${meeting.getDialNumber()}</dialNumber>
|
||||
<attendeePW>${meeting.getViewerPassword()}</attendeePW>
|
||||
<moderatorPW>${meeting.getModeratorPassword()}</moderatorPW>
|
||||
<running>${meeting.isRunning()?c}</running>
|
||||
<duration>${meeting.getDuration()}</duration>
|
||||
<hasUserJoined>${meeting.hasUserJoined()?c}</hasUserJoined>
|
||||
<recording>${meeting.isRecord()?c}</recording>
|
||||
<hasBeenForciblyEnded>${meeting.isForciblyEnded()?c}</hasBeenForciblyEnded>
|
||||
<startTime>${meeting.getStartTime()?c}</startTime>
|
||||
<endTime>${meeting.getEndTime()}</endTime>
|
||||
<participantCount>${meeting.getNumUsers()}</participantCount>
|
||||
<listenerCount>${meeting.getNumListenOnly()}</listenerCount>
|
||||
<voiceParticipantCount>${meeting.getNumVoiceJoined()}</voiceParticipantCount>
|
||||
<videoCount>${meeting.getNumVideos()}</videoCount>
|
||||
<maxUsers>${meeting.getMaxUsers()}</maxUsers>
|
||||
<moderatorCount>${meeting.getNumModerators()}</moderatorCount>
|
||||
<attendees>
|
||||
<#list meetingDetail.meeting.getUsers() as att>
|
||||
<attendee>
|
||||
<userID>${att.getInternalUserId()}</userID>
|
||||
<fullName>${att.getFullname()}</fullName>
|
||||
<role>${att.getRole()}</role>
|
||||
<isPresenter>${att.isPresenter()?c}</isPresenter>
|
||||
<isListeningOnly>${att.isListeningOnly()?c}</isListeningOnly>
|
||||
<hasJoinedVoice>${att.isVoiceJoined()?c}</hasJoinedVoice>
|
||||
<hasVideo>${att.hasVideo()?c}</hasVideo>
|
||||
<#if meeting.getUserCustomData(att.getExternalUserId())??>
|
||||
<#assign ucd = meetingDetail.meeting.getUserCustomData(att.getExternalUserId())>
|
||||
<customdata>
|
||||
<#list ucd?keys as prop>
|
||||
<${prop}><![CDATA[${ucd[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</customdata>
|
||||
</#if>
|
||||
</attendee>
|
||||
</#list>
|
||||
</attendees>
|
||||
<#assign m = meetingDetail.meeting.getMetadata()>
|
||||
<metadata>
|
||||
<#list m?keys as prop>
|
||||
<${prop}><![CDATA[${m[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</metadata>
|
||||
|
||||
<#if meetingDetail.meeting.isBreakout()>
|
||||
<breakout>
|
||||
<parentMeetingID>${meetingDetail.meeting.getParentMeetingId()}</parentMeetingID>
|
||||
<sequence>${meetingDetail.meeting.getSequence()}</sequence>
|
||||
</breakout>
|
||||
</#if>
|
||||
|
||||
<#list meetingDetail.meeting.getBreakoutRooms()>
|
||||
<breakoutRooms>
|
||||
<#items as room>
|
||||
<breakout>${room}</breakout>
|
||||
</#items>
|
||||
</breakoutRooms>
|
||||
</#list>
|
||||
</meeting>
|
||||
</#items>
|
||||
</meetings>
|
||||
</#list>
|
||||
</response>
|
90
bigbluebutton-web/web-app/WEB-INF/freemarker/get-recordings.ftl
Normal file → Executable file
90
bigbluebutton-web/web-app/WEB-INF/freemarker/get-recordings.ftl
Normal file → Executable file
@ -2,62 +2,64 @@
|
||||
<#compress>
|
||||
<response>
|
||||
<#-- Where code is a 'SUCCESS' or 'FAILED' String -->
|
||||
<returncode>${code}</returncode>
|
||||
<returncode>${returnCode}</returncode>
|
||||
<recordings>
|
||||
<#-- Where recs is a String -> Recording HashMap -->
|
||||
<#list recs as r>
|
||||
<#list recordings as r>
|
||||
<recording>
|
||||
<recordID>${r.getId()}</recordID>
|
||||
<meetingID><#if r.getMeetingID()?? && r.getMeetingID() != "">${r.getMeetingID()?html}</#if></meetingID>
|
||||
<name><#if r.getName()?? && r.getName() != ""><![CDATA[${r.getName()}]]></#if></name>
|
||||
<published>${r.isPublished()?string}</published>
|
||||
<meetingID>${r.getMeeting().getId()?html}</meetingID>
|
||||
<externalMeetingID>${r.getMeeting().getExternalId()?html}</externalMeetingID>
|
||||
<name><![CDATA[${r.getMeeting().getName()}]]></name>
|
||||
<isBreakout>${r.getMeeting().isBreakout()?c}</isBreakout>
|
||||
<published>${r.getPublished()?string}</published>
|
||||
<state>${r.getState()?string}</state>
|
||||
<startTime><#if r.getStartTime()?? && r.getStartTime() != "">${r.getStartTime()}</#if></startTime>
|
||||
<endTime><#if r.getEndTime()?? && r.getEndTime() != "">${r.getEndTime()}</#if></endTime>
|
||||
<participants><#if r.getNumParticipants()??>${r.getNumParticipants()}</#if></participants>
|
||||
<#assign m = r.getMetadata()>
|
||||
<participants><#if r.getParticipants()??>${r.getParticipants()}</#if></participants>
|
||||
<#if r.getBreakout()??>
|
||||
<#assign breakout = r.getBreakout()>
|
||||
<breakout>
|
||||
<parentId>${breakout.getParentMeetingId()}</parentId>
|
||||
<sequence>${breakout.getSequence()?c}</sequence>
|
||||
</breakout>
|
||||
</#if>
|
||||
<#if r.getBreakoutRooms()??>
|
||||
<#list r.getBreakoutRooms()>
|
||||
<breakoutRooms>
|
||||
<#items as broom>
|
||||
<breakoutRoom>${broom.getValue()}</breakoutRoom>
|
||||
</#items>
|
||||
</breakoutRooms>
|
||||
</#list>
|
||||
</#if>
|
||||
<#assign m = r.getMeta().get()>
|
||||
<metadata>
|
||||
<#list m?keys as prop>
|
||||
<${prop}><![CDATA[${m[prop]}]]></${prop}>
|
||||
</#list>
|
||||
</metadata>
|
||||
<#assign pb = r.getPlayback()>
|
||||
<playback>
|
||||
<#if r.getPlaybacks()??>
|
||||
<#list r.getPlaybacks() as p>
|
||||
<#if p?? && p.getFormat()??>
|
||||
<format>
|
||||
<type>${p.getFormat()}</type>
|
||||
<url>${p.getUrl()}</url>
|
||||
<length>${p.getLength()}</length>
|
||||
<#if p.getExtensions()??>
|
||||
<#list p.getExtensions() as extension>
|
||||
<${extension.getType()}>
|
||||
<#assign properties = extension.getProperties()>
|
||||
<#if extension.getType() == "preview">
|
||||
<#list properties?keys as property>
|
||||
<#if property == "images">
|
||||
<${property}>
|
||||
<#if properties[property]["image"]?? && properties[property]["image"]?is_hash>
|
||||
<#assign image = properties[property]["image"]>
|
||||
<image <#if image["attributes"]?? && image["attributes"]["width"]??>width="${image["attributes"]["width"]}"</#if> <#if image["attributes"]?? && image["attributes"]["height"]??>height="${image["attributes"]["height"]}"</#if> <#if image["attributes"]?? && image["attributes"]["alt"]??>alt="<#escape x as x?xml>${image["attributes"]["alt"]}</#escape>"</#if>>${image["text"]}</image>
|
||||
<#elseif properties[property]["image"]?? && properties[property]["image"]?is_enumerable>
|
||||
<#list properties[property]["image"] as image>
|
||||
<image <#if image["attributes"]?? && image["attributes"]["width"]??>width="${image["attributes"]["width"]}"</#if> <#if image["attributes"]?? && image["attributes"]["height"]??>height="${image["attributes"]["height"]}"</#if> <#if image["attributes"]?? && image["attributes"]["alt"]??>alt="<#escape x as x?xml>${image["attributes"]["alt"]}</#escape>"</#if>>${image["text"]}</image>
|
||||
</#list>
|
||||
</#if>
|
||||
</${property}>
|
||||
<#else>
|
||||
<${property} />
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
</${extension.getType()}>
|
||||
</#list>
|
||||
</#if>
|
||||
</format>
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
<format>${pb.getFormat()}</format>
|
||||
<link>${pb.getLink()}</link>
|
||||
<processingTime>${pb.getProcessingTime()?c}</processingTime>
|
||||
<duration>${pb.getDuration()?c}</duration>
|
||||
<#if pb.getExtensions()??>
|
||||
<extensions>
|
||||
<#if pb.getExtensions().getPreview()??>
|
||||
<#assign prev = pb.getExtensions().getPreview()>
|
||||
<preview>
|
||||
<#list prev.getImages()>
|
||||
<images>
|
||||
<#items as image>
|
||||
<image width="${image.getWidth()}" height="${image.getHeight()}" alt="${image.getAlt()}">${image.getValue()}</image>
|
||||
</#items>
|
||||
</images>
|
||||
</#list>
|
||||
</preview>
|
||||
</#if>
|
||||
</extensions>
|
||||
</#if>
|
||||
</playback>
|
||||
</recording>
|
||||
</#list>
|
||||
|
@ -50,10 +50,22 @@ module BigBlueButton
|
||||
@redis.hgetall("meeting:info:#{meeting_id}")
|
||||
end
|
||||
|
||||
def has_breakout_metadata_for(meeting_id)
|
||||
@redis.exists("meeting:breakout:#{meeting_id}")
|
||||
end
|
||||
|
||||
def breakout_metadata_for(meeting_id)
|
||||
@redis.hgetall("meeting:breakout:#{meeting_id}")
|
||||
end
|
||||
|
||||
def has_breakout_rooms_for(meeting_id)
|
||||
@redis.exists("meeting:breakout:rooms:#{meeting_id}")
|
||||
end
|
||||
|
||||
def breakout_rooms_for(meeting_id)
|
||||
@redis.smembers("meeting:breakout:rooms:#{meeting_id}")
|
||||
end
|
||||
|
||||
def num_events_for(meeting_id)
|
||||
@redis.llen("meeting:#{meeting_id}:recordings")
|
||||
end
|
||||
@ -82,6 +94,10 @@ module BigBlueButton
|
||||
@redis.del("meeting:breakout:#{meeting_id}")
|
||||
end
|
||||
|
||||
def delete_breakout_rooms_for(meeting_id)
|
||||
@redis.del("meeting:breakout:rooms:#{meeting_id}")
|
||||
end
|
||||
|
||||
def build_header(message_type)
|
||||
return {
|
||||
"timestamp" => BigBlueButton.monotonic_clock, #
|
||||
@ -175,6 +191,8 @@ module BigBlueButton
|
||||
MODULE = 'module'
|
||||
EVENTNAME = 'eventName'
|
||||
MEETINGID = 'meetingId'
|
||||
MEETINGNAME = 'meetingName'
|
||||
ISBREAKOUT = 'isBreakout'
|
||||
|
||||
def initialize(redis)
|
||||
@redis = redis
|
||||
@ -186,13 +204,29 @@ module BigBlueButton
|
||||
result = xml.instruct! :xml, :version => "1.0", :encoding=>"UTF-8"
|
||||
|
||||
meeting_metadata = @redis.metadata_for(meeting_id)
|
||||
breakout_metadata = @redis.breakout_metadata_for(meeting_id)
|
||||
version = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))["bbb_version"]
|
||||
|
||||
if (meeting_metadata != nil)
|
||||
xml.recording(:meeting_id => meeting_id, :bbb_version => version) {
|
||||
xml.meeting(:id => meeting_id, :externalId => meeting_metadata[MEETINGID], :name => meeting_metadata[MEETINGNAME], :breakout => meeting_metadata[ISBREAKOUT])
|
||||
xml.metadata(meeting_metadata)
|
||||
xml.breakout(breakout_metadata)
|
||||
|
||||
if (@redis.has_breakout_metadata_for(meeting_id))
|
||||
breakout_metadata = @redis.breakout_metadata_for(meeting_id)
|
||||
xml.breakout(breakout_metadata)
|
||||
end
|
||||
|
||||
if (@redis.has_breakout_rooms_for(meeting_id))
|
||||
breakout_rooms = @redis.breakout_rooms_for(meeting_id)
|
||||
if (breakout_rooms != nil)
|
||||
xml.breakoutRooms() {
|
||||
breakout_rooms.each do |breakout_room|
|
||||
xml.breakoutRoom(breakout_room)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
msgs = @redis.events_for(meeting_id)
|
||||
msgs.each do |msg|
|
||||
res = @redis.event_info_for(meeting_id, msg)
|
||||
@ -231,6 +265,7 @@ module BigBlueButton
|
||||
end
|
||||
@redis.delete_metadata_for(meeting_id)
|
||||
@redis.delete_breakout_metadata_for(meeting_id)
|
||||
@redis.delete_breakout_rooms_for(meeting_id)
|
||||
end
|
||||
|
||||
def save_events_to_file(directory, result)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Set encoding to utf-8
|
||||
# Set encoding to utf-8
|
||||
# encoding: UTF-8
|
||||
|
||||
#
|
||||
@ -109,6 +109,24 @@ if not FileTest.directory?(target_dir)
|
||||
end_time = recording.at_xpath("end_time")
|
||||
end_time.content = real_end_time
|
||||
|
||||
## Copy the breakout and breakout rooms node from
|
||||
## events.xml if present.
|
||||
breakout_xpath = @doc.xpath("//breakout")
|
||||
breakout_rooms_xpath = @doc.xpath("//breakoutRooms")
|
||||
meeting_xpath = @doc.xpath("//meeting")
|
||||
|
||||
if (meeting_xpath != nil)
|
||||
recording << meeting_xpath
|
||||
end
|
||||
|
||||
if (breakout_xpath != nil)
|
||||
recording << breakout_xpath
|
||||
end
|
||||
|
||||
if (breakout_rooms_xpath != nil)
|
||||
recording << breakout_rooms_xpath
|
||||
end
|
||||
|
||||
participants = recording.at_xpath("participants")
|
||||
participants.content = BigBlueButton::Events.get_num_participants("#{target_dir}/events.xml")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user