- publish/unpublish recording

This commit is contained in:
Richard Alam 2017-03-15 14:29:10 +00:00
parent 1ccf55fd97
commit 058a4044d6
9 changed files with 252 additions and 61 deletions

View File

@ -32,11 +32,14 @@ libraryDependencies += "org.scala-lang" % "scala-library" % scalaV
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaV
libraryDependencies += "commons-lang" % "commons-lang" % "2.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"

View File

@ -92,7 +92,7 @@ public class RecordingService {
return recs;
}
private RecordingMetadata getRecordingMetadata(File dir) {
private static RecordingMetadata getRecordingMetadata(File dir) {
File file = new File(dir.getPath() + File.separatorChar + "metadata.xml");
RecordingMetadata rec = RecordingMetadataReaderHelper.getRecordingMetadata(file);
return rec;
@ -262,7 +262,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]);
@ -275,12 +275,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.
@ -297,7 +297,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));
@ -313,7 +313,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()];
@ -388,41 +388,94 @@ 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() + File.separatorChar + recordingId);
RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml);
if (r != null) {
if (!destDir.exists()) destDir.mkdirs();
boolean moved = recordingDir.renameTo(destDir);
if (moved) {
log.debug("Recording successfully moved!");
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);
log.debug(String.format("Published successfully %s!", recordingId));
} else {
log.debug("Recording was not moved");
}
}
}
public static void unpublishRecording(File destDir, String recordingId, File recordingDir) {
File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId);
RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml);
if (r != null) {
if (!destDir.exists()) destDir.mkdirs();
boolean moved = recordingDir.renameTo(destDir);
if (moved) {
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);
log.debug(String.format("Unpublished successfully %s!", recordingId));
} else {
log.debug("Recording was not moved");
}
}
}
public static void deleteRecording(File destDir, String recordingId, File recordingDir) {
File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId);
RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml);
if (r != null) {
if (!destDir.exists()) destDir.mkdirs();
boolean moved = recordingDir.renameTo(destDir);
if (moved) {
r.setState(Recording.STATE_DELETED);
r.setPublished(false);
deleteRecording(recordingId, destDir.getAbsolutePath());
File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(destDir.getAbsolutePath() + File.separatorChar + recordingId);
// Process the changes by saving the recording into metadata.xml
RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r);
log.debug(String.format("Unpublished successfully %s!", recordingId));
} else {
log.debug("Recording was not moved");
}
}
}
private List<File> getAllDirectories(String state) {
List<File> allDirectories = new ArrayList<File>();
@ -488,22 +541,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);
}
}
}
@ -511,6 +550,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>();

View File

@ -20,4 +20,12 @@ public class Metadata {
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);
}
}

View File

@ -4,10 +4,9 @@ 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.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.*;
import java.io.*;
public class RecordingMetadataReaderHelper {
@ -48,4 +47,29 @@ public class RecordingMetadataReaderHelper {
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) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -6,9 +6,6 @@ import javax.xml.stream.{XMLInputFactory, XMLStreamReader}
import com.fasterxml.jackson.dataformat.xml.{JacksonXmlModule, XmlMapper}
import org.bigbluebutton.api.domain.{RecordingMetadata, RecordingMetadataPlayback}
/**
* Created by ralam on 3/10/2017.
*/
class RecordingMetadataReaderHelperTest extends UnitSpec {
it should "deserialize playback part of metadata.xml" in {
@ -53,4 +50,30 @@ class RecordingMetadataReaderHelperTest extends UnitSpec {
}
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())
}
}

View File

@ -0,0 +1,74 @@
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 publishedDir = new File("target/published")
RecordingService.publishRecording(publishedDir, recordingId, destDir)
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 unpublishedDir = new File("target/unpublished")
if (unpublishedDir.exists()) FileUtils.deleteDirectory(unpublishedDir)
RecordingService.unpublishRecording(unpublishedDir, recordingId, destDir)
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 deletedDir = new File("target/deleted")
if (deletedDir.exists()) FileUtils.deleteDirectory(deletedDir)
RecordingService.deleteRecording(deletedDir, recordingId, destDir)
assert(deletedDir.exists())
}
}

View File

@ -1772,7 +1772,7 @@ class ApiController {
}
List<RecordingMetadata> recsList = meetingService.getRecordingsMetadata(internalRecordIds, states);
List<RecordingMetadata>recs = meetingService.filterRecordingsByMetadata(recsList, ParamsProcessorUtil.processMetaParam(params));
List<RecordingMetadata> recs = meetingService.filterRecordingsByMetadata(recsList, ParamsProcessorUtil.processMetaParam(params));
if (recs.isEmpty()) {
response.addHeader("Cache-Control", "no-cache")
@ -1794,7 +1794,7 @@ class ApiController {
def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker")
ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc))
def xmlText = responseBuilder.buildGetRecordingsResponse(recsList, "success")
def xmlText = responseBuilder.buildGetRecordingsResponse(recs, "success")
withFormat {
xml {
render(text: xmlText, contentType: "text/xml")

View File

@ -517,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);
}