package com.brightcove.opensource.workflowengine.actors;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.apache.commons.io.FileUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.brightcove.commons.applications.FTPUploader;
import com.brightcove.commons.catalog.objects.Video;
import com.brightcove.commons.xml.XalanUtils;
import com.brightcove.opensource.workflowengine.Actor;
import com.brightcove.opensource.workflowengine.Workflow;
import com.brightcove.opensource.workflowengine.record.Property;
import com.brightcove.opensource.workflowengine.record.Record;
/**
* <p>
* Takes each Record provided by downstream providers and uses it to
* issue a command to re-encode that video from existing source.
* </p>
*
* <p>
* This is an intensive job on the Brightcove System, so a delay should
* be introduced between each job request. The length of that delay should
* be worked out with Brightcove Consulting or Brightcove Customer Support,
* but should be AT LEAST as long as the average length of the videos
* being re-encoded
* </p>
*
* <p>
* The following configuration properties will be honored:<ul>
* <li>video-reference-id-prop - Property name on each record that
* stores the video reference id to be re-encoded.</li>
* <li>batch-username - Batch provisioning FTP username.</li>
* <li>batch-password - Batch provisioning FTP password.</li>
* <li>publisher-id - Video Cloud publisher id (account id).</li>
* <li>preparer - Your name and company.</li>
* <li>notification-email - Video Cloud will send email when re-encode for each title is underway.</li>
* <li>overwrite-images - If TRUE, existing images will be overwritten by transcode process.</li>
* <li>preserve-source-as-rendition - If TRUE, mezzanine file will appear as one of transcoded video's renditions.</li>
* </ul>
* </p>
*
* @author <a href="https://github.com/three4clavin">Sander Gates</a>
*
*/
public class VideoCloudReEncodeFromExistingSourceAdapter extends Actor {
private String videoRefIdName = null;
private String batchUsername = null;
private String batchPassword = null;
private String publisherId = null;
private String preparer = null;
private String notifyEmail = null;
private Boolean overwriteImages = null;
private Boolean preserveSource = null;
/**
* <p>
* Default constructor - workflow this actor is part of must be
* provided
* </p>
*
* @param workflow Workflow this actor will be part of
*/
public VideoCloudReEncodeFromExistingSourceAdapter(Workflow workflow){
super(workflow);
}
/* (non-Javadoc)
* @see com.brightcove.opensource.workflowengine.Actor#run()
*/
public void run(){
if(! hasStarted()){
videoRefIdName = getFirstPropertyValue("video-reference-id-prop");
batchUsername = getFirstPropertyValue("batch-username");
batchPassword = getFirstPropertyValue("batch-password");
publisherId = getFirstPropertyValue("publisher-id");
preparer = getFirstPropertyValue("preparer");
notifyEmail = getFirstPropertyValue("notification-email");
if(videoRefIdName == null){
die("'video-reference-id-prop' must be specified to the VideoCloudReEncodeFromExistingSource actor.");
}
if((batchUsername == null) || (batchPassword == null)){
die("'batch-username' and 'batch-password' must be specified to the VideoCloudReEncodeFromExistingSource actor.");
}
if(publisherId == null){
die("'publisher-id' must be specified to the VideoCloudReEncodeFromExistingSource actor.");
}
if(preparer == null){
die("'preparer' must be specified to the VideCloudReEncodeFromExistingSource actor.");
}
if("TRUE".equalsIgnoreCase(getFirstPropertyValue("overwrite-images"))){
overwriteImages = true;
}
else{
overwriteImages = false;
}
if("TRUE".equalsIgnoreCase(getFirstPropertyValue("preserve-source-as-rendition"))){
preserveSource = true;
}
else{
preserveSource = false;
}
}
super.run();
}
/* (non-Javadoc)
* @see com.brightcove.opensource.workflowengine.Actor#handleRecord(com.brightcove.opensource.workflowengine.Record)
*/
public void handleRecord(Record record){
Property<?> videoRefIdProp = record.getFirstProperty(videoRefIdName);
Video video = new Video();
if((videoRefIdProp != null) && (videoRefIdProp.getValue() != null) && (! "".equals(videoRefIdProp.getValue()))){
video.setReferenceId(""+videoRefIdProp.getValue());
}
else{
logError("Couldn't get ReferenceId from record '" + record + "'.");
return;
}
log("Generating re-encode request for video '" + video + "'.");
Document doc = null;
try {
doc = XalanUtils.createDocument("publisher-upload-manifest");
}
catch (ParserConfigurationException pce) {
die("Couldn't create publisher manifest file. Exception caught: '" + pce + "'.");
}
Element root = doc.getDocumentElement();
root.setAttribute("publisher-id", publisherId);
root.setAttribute("preparer", preparer);
root.setAttribute("report-success", "TRUE");
if(notifyEmail != null){
Element notifyElem = XalanUtils.createSimpleElement(doc, "notify", null, root);
notifyElem.setAttribute("email", notifyEmail);
}
Element reEncodeElem = XalanUtils.createSimpleElement(doc, "reencode-from-existing-source", null, root);
reEncodeElem.setAttribute("title-refid", video.getReferenceId());
reEncodeElem.setAttribute("encode-to", "MP4");
reEncodeElem.setAttribute("encode-multiple", "TRUE");
if(overwriteImages){
reEncodeElem.setAttribute("overwrite-images", "TRUE");
}
else{
reEncodeElem.setAttribute("overwrite-images", "FALSE");
}
if(preserveSource){
reEncodeElem.setAttribute("preserve-source-as-rendition", "TRUE");
}
else{
reEncodeElem.setAttribute("preserve-source-as-rendition", "FALSE");
}
File batchFile = new File("VideoCloudReEncodeFromExistingSource-tmp.xml");
try {
FileUtils.writeStringToFile(batchFile, XalanUtils.prettyPrintWithTrAX(doc), "UTF-8");
}
catch (IOException ioe) {
die("Couldn't write batch XML file: '" + ioe + "'.");
}
catch (TransformerException te) {
die("Couldn't write batch XML file: '" + te + "'.");
}
catch (ParserConfigurationException pce) {
die("Couldn't write batch XML file: '" + pce + "'.");
}
Long ftpTimeout = 1000l * 60l * 60l; // 1 hour
FTPUploader ftpUploader = new FTPUploader("upload.brightcove.com", 21, batchUsername, batchPassword, false, false, false, ftpTimeout, true);
ftpUploader.calculateUploadMappings(batchFile.getParent(), batchFile.getName(), null, "/");
// Bug somewhere in the timeout logic setting from the constructor...
ftpUploader.setTimeoutMilliseconds(ftpTimeout);
try {
ftpUploader.doUpload();
}
catch (Exception e) {
die("Couldn't upload '" + batchFile.getAbsolutePath() + "'. Exception caught: '" + e + "'.");
}
batchFile.delete();
}
}