Skip to content

Commit

Permalink
bbb-web: Added code for updating recording metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
jfederico committed Jul 11, 2016
1 parent 51ee64d commit 34ad746
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 46 deletions.
17 changes: 10 additions & 7 deletions bigbluebutton-web/grails-app/conf/UrlMappings.groovy
Expand Up @@ -52,16 +52,19 @@ class UrlMappings {
"/api/getMeetings"(controller:"api") {
action = [GET:'getMeetingsHandler', POST:'getMeetingsHandler']
}


"/api/getSessions"(controller:"api") {
action = [GET:'getSessionsHandler', POST:'getSessionsHandler']
}


"/api/getSessions"(controller:"api") {
action = [GET:'getSessionsHandler', POST:'getSessionsHandler']
}

"/api/getRecordings"(controller:"api") {
action = [GET:'getRecordingsHandler', POST:'getRecordingsHandler']
}


"/api/updateRecordings"(controller:"api") {
action = [GET:'updateRecordingsHandler', POST:'updateRecordingsHandler']
}

"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
Expand Down
Expand Up @@ -1767,7 +1767,6 @@ class ApiController {
/******************************************************
* PUBLISH_RECORDINGS API
******************************************************/

def publishRecordings = {
String API_CALL = "publishRecordings"
log.debug CONTROLLER_NAME + "#${API_CALL}"
Expand Down Expand Up @@ -1924,6 +1923,85 @@ class ApiController {
}
}

/******************************************************
* UPDATE_RECORDINGS API
******************************************************/
def updateRecordingsHandler = {
String API_CALL = "updateRecordings"
log.debug CONTROLLER_NAME + "#${API_CALL}"

// BEGIN - backward compatibility
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
return
}

if (StringUtils.isEmpty(params.recordID)) {
invalid("missingParamRecordID", "You must specify a recordID.");
return
}

if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
// END - backward compatibility

ApiErrors errors = new ApiErrors()

// Do we have a checksum? If none, complain.
if (StringUtils.isEmpty(params.checksum)) {
errors.missingParamError("checksum");
}

// Do we have a recording id? If none, complain.
String recordId = params.recordID
if (StringUtils.isEmpty(recordId)) {
errors.missingParamError("recordID");
}

if (errors.hasErrors()) {
respondWithErrors(errors)
return
}

// Do we agree on the checksum? If not, complain.
if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
errors.checksumError()
respondWithErrors(errors)
return
}

List<String> recordIdList = new ArrayList<String>();
if (!StringUtils.isEmpty(recordId)) {
recordIdList=paramsProcessorUtil.decodeIds(recordId);
}

if (!meetingService.existsAnyRecording(recordIdList)) {
// BEGIN - backward compatibility
invalid("notFound", "We could not find recordings");
return;
// END - backward compatibility
}

//Execute code specific for this call
Map<String, String> metaParams = ParamsProcessorUtil.processMetaParam(params)
if ( !metaParams.empty ) {
//Proceed with the update
meetingService.updateRecordings(recordIdList, metaParams);
}
withFormat {
xml {
render(contentType:"text/xml") {
response() {
returncode(RESP_CODE_SUCCESS)
updated(true)
}
}
}
}
}

def uploadDocuments(conf) {
log.debug("ApiController#uploadDocuments(${conf.getInternalId()})");

Expand Down
Expand Up @@ -27,9 +27,9 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -292,7 +292,7 @@ public void createMeeting(Meeting m) {
private void handleCreateMeeting(Meeting m) {
meetings.put(m.getInternalId(), m);
if (m.isRecord()) {
Map<String, String> metadata = new LinkedHashMap<String, String>();
Map<String, String> metadata = new TreeMap<String, String>();
metadata.putAll(m.getMetadata());
// TODO: Need a better way to store these values for recordings
metadata.put("meetingId", m.getExternalId());
Expand Down Expand Up @@ -421,7 +421,7 @@ public Map<String, Recording> reorderRecordings(List<Recording> olds) {
r.setMeetingID(mid);
r.setName(name);

ArrayList<Playback> plays = new ArrayList<Playback>();
List<Playback> plays = new ArrayList<Playback>();

if (r.getPlaybackFormat() != null) {
plays.add(new Playback(r.getPlaybackFormat(), r
Expand Down Expand Up @@ -471,20 +471,24 @@ public boolean existsAnyRecording(List<String> idList) {

public void setPublishRecording(List<String> idList, boolean publish) {
for (String id : idList) {
if (publish) {
if (publish) {
recordingService.changeState(id, Recording.STATE_PUBLISHED);
} else {
} else {
recordingService.changeState(id, Recording.STATE_UNPUBLISHED);
}
}
}
}
}

public void deleteRecordings(ArrayList<String> idList){
for (String id : idList) {
recordingService.changeState(id, Recording.STATE_DELETED);
public void deleteRecordings(List<String> idList) {
for (String id : idList) {
recordingService.changeState(id, Recording.STATE_DELETED);
}
}

public void updateRecordings(List<String> idList, Map<String, String> metaParams) {
recordingService.updateMetaParams(idList, metaParams);
}

public void processRecording(String meetingId) {
recordingService.startIngestAndProcessing(meetingId);
}
Expand Down
108 changes: 89 additions & 19 deletions bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java
Expand Up @@ -76,8 +76,14 @@ public List<Recording> getRecordings(List<String> recordIDs, List<String> states

for (String recordID : recordIDs) {
for (Map.Entry<String, List<File>> entry : allDirectories.entrySet()) {
List<Recording> _recs = getRecordingsForPath(recordID, entry.getValue());
recs.addAll(_recs);
List<File> _recs = getRecordingsForPath(recordID, entry.getValue());
Iterator<File> iterator = _recs.iterator();
while (iterator.hasNext()) {
Recording r = getRecordingInfo(iterator.next());
if (r != null) {
recs.add(r);
}
}
}
}

Expand Down Expand Up @@ -165,16 +171,14 @@ private Set<String> getAllRecordingIds(List<File> recs) {
return ids;
}

private List<Recording> getRecordingsForPath(String id, List<File> recordings) {
List<Recording> recs = new ArrayList<Recording>();
private List<File> getRecordingsForPath(String id, List<File> recordings) {
List<File> recs = new ArrayList<File>();

Iterator<File> iterator = recordings.iterator();
while (iterator.hasNext()) {
File recording = iterator.next();
if (recording.getName().startsWith(id)) {
Recording r = getRecordingInfo(recording);
if (r != null)
recs.add(r);
File rec = iterator.next();
if (rec.getName().startsWith(id)) {
recs.add(rec);
}
}
return recs;
Expand Down Expand Up @@ -349,16 +353,7 @@ private void changeState(String path, String recordingId, String state) {
private List<File> getAllDirectories(String state) {
List<File> allDirectories = new ArrayList<File>();

String dir = null;
if( state.equals(Recording.STATE_PUBLISHED) ) {
dir = publishedDir;
} else if ( state.equals(Recording.STATE_UNPUBLISHED) ){
dir = unpublishedDir;
} else if ( state.equals(Recording.STATE_DELETED) ){
dir = deletedDir;
} else if ( state.equals(Recording.STATE_PROCESSING) || state.equals(Recording.STATE_PROCESSED) ){
dir = processDir;
}
String dir = getDestinationBaseDirectoryName(state);

if ( dir != null ) {
String[] formats = getPlaybackFormats(dir);
Expand Down Expand Up @@ -401,4 +396,79 @@ private Map<String, List<File>> getAllDirectories(List<String> states) {
return allDirectories;
}

public void updateMetaParams(List<String> recordIDs, Map<String,String> metaParams) {
updateMetaParams(recordIDs, metaParams, false);
}

public void updateMetaParams(List<String> recordIDs, Map<String,String> metaParams, boolean forced) {

// Define the directories used to lookup the recording
List<String> states = new ArrayList<String>();
states.add(Recording.STATE_PUBLISHED);
states.add(Recording.STATE_UNPUBLISHED);
states.add(Recording.STATE_DELETED);

// Gather all the existent directories based on the states defined for the lookup
Map<String, List<File>> allDirectories = getAllDirectories(states);

// Retrieve the actual recording from the directories gathered for the lookup
for (String recordID : recordIDs) {
for (Map.Entry<String, List<File>> entry : allDirectories.entrySet()) {
List<File> recs = getRecordingsForPath(recordID, entry.getValue());
// Lookup the target recording
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 ( rec.containsMetadata(meta.getKey()) || forced ) {
// The meta parameter already exists, update it
// Or if doesn't exist but forced, then add it
rec.updateMetadata(meta.getKey(), meta.getValue());
}
}
// Process the changes by saving the recording into metadata.xml
recordingServiceHelper.writeRecordingInfo(recFile.getAbsolutePath(), rec);
}
}
}
}

return;
}

private Map<String,File> indexRecordings(List<File> recs) {
Map<String,File> indexedRecs = new HashMap<String,File>();

Iterator<File> iterator = recs.iterator();
while (iterator.hasNext()) {
File rec = iterator.next();
indexedRecs.put(rec.getName(), rec);
}

return indexedRecs;
}

private String getDestinationBaseDirectoryName(String state) {
return getDestinationBaseDirectoryName(state, false);
}

private String getDestinationBaseDirectoryName(String state, boolean forceDefault) {
String baseDir = null;

if ( state.equals(Recording.STATE_PROCESSING) || state.equals(Recording.STATE_PROCESSED) )
baseDir = processDir;
else if ( state.equals(Recording.STATE_PUBLISHED) )
baseDir = publishedDir;
else if ( state.equals(Recording.STATE_UNPUBLISHED) )
baseDir = unpublishedDir;
else if ( state.equals(Recording.STATE_DELETED) )
baseDir = deletedDir;
else if ( forceDefault )
baseDir = publishedDir;

return baseDir;
}

}
Expand Up @@ -23,8 +23,9 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import groovy.util.slurpersupport.GPathResult;

Expand All @@ -35,8 +36,8 @@ public class Recording {
private boolean published;
private String startTime;
private String endTime;
private Map<String, String> metadata = new HashMap<String, String>();
private ArrayList<Playback> playbacks=new ArrayList<Playback>();
private Map<String, String> metadata = new TreeMap<String, String>();
private List<Playback> playbacks=new ArrayList<Playback>();

//TODO:
private String state;
Expand Down Expand Up @@ -131,34 +132,46 @@ public void setPlaybackExtensions(GPathResult playbackExtensions) {
}

public Map<String, String> getMetadata() {
return metadata;
return this.metadata;
}


public String getMetadata(String key) {
return this.metadata.get(key);
}

public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}

public void updateMetadata(String key, String value) {
this.metadata.put(key, value);
}

public boolean containsMetadata(String key) {
return this.metadata.containsKey(key);
}

public String getMeetingID() {
return meetingID;
return this.meetingID;
}

public void setMeetingID(String meetingID) {
this.meetingID = meetingID;
}

public String getName() {
return name;
return this.name;
}

public void setName(String name) {
this.name = name;
}

public ArrayList<Playback> getPlaybacks() {
public List<Playback> getPlaybacks() {
return playbacks;
}

public void setPlaybacks(ArrayList<Playback> playbacks) {
public void setPlaybacks(List<Playback> playbacks) {
this.playbacks = playbacks;
}

Expand Down

0 comments on commit 34ad746

Please sign in to comment.