Today we have a question from maumig who asks if there is a way to get a video storyboard through Client Automation or REST API. And as you know the focus on our latest release has been on the REST API. So I’m going to answer today’s question using some of the new features we’ve introduced in this release.
So the first thing I want to show you is how to get a document. We’ve added a bunch of endpoints for resources like document, group, user, operation, and directory, so if you want to retrieve a document, you can either use its ID or path:
GET /nuxeo/api/id/{idOfTheDoc}
GET /nuxeo/api/path/{pathOfTheDoc}
This will return a JSON object looking like this:
{ "entity-type" : "document",
"facets" : [ "HasVideoPreview",
"Commentable",
"Asset",
"HasStoryboard",
"Versionable",
"Publishable",
"Video"
],
"lastModified" : "2013-09-25T17:01:35.13Z",
"path" : "/default-domain/UserWorkspaces/Administrator/aa",
"repository" : "default",
"state" : "project",
"title" : "aa",
"type" : "Video",
"uid" : "b535960e-975b-49a6-a0d2-e508f369302e",
"versionLabel" : "0.0",
"changeToken" : "1380128495133",
"contextParameters" : {}
}
As you can see we have no information concerning the storyboard. But we do have an empty contextParameters object. It can be filled with other objects using a new extension point:
<extension target="org.nuxeo.ecm.automation.io.services.contributor.RestContributorService" point="contributor">
<contributor name="storyboard" class="org.nuxeo.sample.StoryboardContributor">
<category>Video</category>
</contributor>
</extension>
The class must implement the RestContributor interface. The contribute method gives you access to the RestEvaluationContext, which lets you retrieve the document and the request headers, and a JsonGenerator. So what we’re doing here is creating a JSON Object with the document in the getJsonStoryboard method and writing the result with the JsonGenerator.
package org.nuxeo.sample;
import java.io.IOException;
import java.util.List;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ObjectNode;
import org.nuxeo.ecm.automation.io.services.contributor.RestContributor;
import org.nuxeo.ecm.automation.io.services.contributor.RestEvaluationContext;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.model.PropertyException;
import org.nuxeo.ecm.platform.video.VideoConstants;
import org.nuxeo.runtime.api.Framework;
public class StoryboardContributor implements RestContributor {
@Override
public void contribute(JsonGenerator jg, RestEvaluationContext ec)
throws ClientException, IOException {
DocumentModel doc = ec.getDocumentModel();
ObjectNode storyBoardJsonObject = getJsonStoryboard(doc);
jg.writeObject(storyBoardJsonObject);
}
private ObjectNode getJsonStoryboard(DocumentModel doc)
throws PropertyException, ClientException {
int size = doc.getProperty(VideoConstants.STORYBOARD_PROPERTY).getValue(
List.class).size();
ObjectMapper o = new ObjectMapper();
ObjectNode storyBoardJsonObject = o.createObjectNode();
for (int i = 0; i < size; i++) {
String propertyPath = VideoConstants.STORYBOARD_PROPERTY + "/" + i;
String blobPropertyName = propertyPath + "/content";
String timecodeProperty = propertyPath + "/timecode";
String filename = String.format("storyboard-%03d.jpeg", i);
String baseURL = Framework.getProperty("nuxeo.url");
String blobUrl = getBigFileUrl(doc, blobPropertyName, filename,
baseURL);
String timecode = "";
Double tc = doc.getProperty(timecodeProperty).getValue(Double.class);
if (tc != null) {
timecode = String.format("%f", Math.floor(tc));
}
ObjectNode thumb = o.createObjectNode();
thumb.put("src", blobUrl);
storyBoardJsonObject.put(timecode.split("\.")[0], thumb);
}
return storyBoardJsonObject;
}
private String getBigFileUrl(DocumentModel doc, String blobPropertyName,
String filename, String baseURL) {
String blobUrl = baseURL;
blobUrl += "/nxbigfile/";
blobUrl += doc.getRepositoryName() + "/";
blobUrl += doc.getId() + "/";
blobUrl += blobPropertyName + "/";
blobUrl += filename;
return blobUrl;
}
}
To make sure your request will use the right contributors, you need to specify the X-NXContext-Category header:
curl --header "X-NXContext-Category:Video" -u Administrator:Administrator http://localhost:8080/nuxeo/api/id/b535960e-975b-49a6-a0d2-e508f369302e
Here we use Video, the category specified in the contribution. You can have more control on these using action filters:
<extension target="org.nuxeo.ecm.automation.io.services.contributor.RestContributorService" point="contributor">
<contributor name="storyboard" class="org.nuxeo.sample.StoryboardContributor">
<category>Video</category>
<filter-id>HasStoryboard</filter-id>
</contributor>
</extension>
<extension target="org.nuxeo.ecm.platform.actions.ActionService"
point="filters">
<filter id="HasStoryboard" append="true">
<rule grant="true">
<facet>HasStoryboard</facet>
</rule>
</filter>
</extension>
This way you’ll know that the storyboard REST contributor will return an object only for documents having the HasStoryboard. The final result should then look like this:
{ "entity-type" : "document",
"facets" : [ "HasVideoPreview",
"Commentable",
"Asset",
"HasStoryboard",
"Versionable",
"Publishable",
"Video"
],
"lastModified" : "2013-09-25T17:01:35.13Z",
"path" : "/default-domain/UserWorkspaces/Administrator/aa",
"repository" : "default",
"state" : "project",
"title" : "aa",
"type" : "Video",
"uid" : "b535960e-975b-49a6-a0d2-e508f369302e",
"versionLabel" : "0.0",
"changeToken" : "1380128495133",
"contextParameters" : { "storyboard" : {
"0" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/0/content/storyboard-000.jpeg" },
"4" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/1/content/storyboard-001.jpeg" },
"9" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/2/content/storyboard-002.jpeg" },
"13" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/3/content/storyboard-003.jpeg" },
"18" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/4/content/storyboard-004.jpeg" },
"22" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/5/content/storyboard-005.jpeg" },
"27" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/6/content/storyboard-006.jpeg" },
"31" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/7/content/storyboard-007.jpeg" },
"36" : { "src" : "http://localhost:8080/nuxeo/nxbigfile/default/b535960e-975b-49a6-a0d2-e508f369302e/vid:storyboard/8/content/storyboard-008.jpeg" }
} },
}
The name of the object in the context parameter is the name of the REST contributor: storyboard. These contributors are really easy to use to extend the existing endpoints. This makes it easy to do things like retrieve all the parent documents and make a breadcrumb, or its open related workflow task.