use of org.opencastproject.workflow.api.WorkflowOperationInstance in project opencast by opencast.
the class VideoEditorWorkflowOperationHandler method resume.
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.ResumableWorkflowOperationHandler#resume(org.opencastproject.workflow.api.WorkflowInstance,
* JobContext, java.util.Map)
*/
@Override
public WorkflowOperationResult resume(WorkflowInstance workflowInstance, JobContext context, Map<String, String> properties) throws WorkflowOperationException {
MediaPackage mp = workflowInstance.getMediaPackage();
logger.info("Resume video editor operation for mediapackage {}", mp.getIdentifier().compact());
// Get configuration
WorkflowOperationInstance worflowOperationInstance = workflowInstance.getCurrentOperation();
String sourceTrackFlavorsProperty = StringUtils.trimToNull(worflowOperationInstance.getConfiguration(SOURCE_FLAVORS_PROPERTY));
if (sourceTrackFlavorsProperty == null) {
throw new WorkflowOperationException(format("Required configuration property %s not set.", SOURCE_FLAVORS_PROPERTY));
}
String targetSmilFlavorProperty = StringUtils.trimToNull(worflowOperationInstance.getConfiguration(TARGET_SMIL_FLAVOR_PROPERTY));
if (targetSmilFlavorProperty == null) {
throw new WorkflowOperationException(format("Required configuration property %s not set.", TARGET_SMIL_FLAVOR_PROPERTY));
}
String targetFlavorSybTypeProperty = StringUtils.trimToNull(worflowOperationInstance.getConfiguration(TARGET_FLAVOR_SUBTYPE_PROPERTY));
if (targetFlavorSybTypeProperty == null) {
throw new WorkflowOperationException(format("Required configuration property %s not set.", TARGET_FLAVOR_SUBTYPE_PROPERTY));
}
boolean skipIfNoTrim = BooleanUtils.toBoolean(worflowOperationInstance.getConfiguration(SKIP_NOT_TRIMMED_PROPERTY));
// Get source tracks
TrackSelector trackSelector = new TrackSelector();
for (String flavor : asList(sourceTrackFlavorsProperty)) {
trackSelector.addFlavor(flavor);
}
Collection<Track> sourceTracks = trackSelector.select(mp, false);
if (sourceTracks.isEmpty()) {
throw new WorkflowOperationException(format("No source tracks found in mediapacksge %s with flavors %s.", mp.getIdentifier().compact(), sourceTrackFlavorsProperty));
}
// Get SMIL file
MediaPackageElementFlavor smilTargetFlavor = MediaPackageElementFlavor.parseFlavor(targetSmilFlavorProperty);
Catalog[] smilCatalogs = mp.getCatalogs(smilTargetFlavor);
if (smilCatalogs == null || smilCatalogs.length == 0) {
throw new WorkflowOperationException(format("No SMIL catalog found in mediapackage %s with flavor %s.", mp.getIdentifier().compact(), targetSmilFlavorProperty));
}
File smilFile = null;
Smil smil = null;
try {
smilFile = workspace.get(smilCatalogs[0].getURI());
smil = smilService.fromXml(smilFile).getSmil();
smil = replaceAllTracksWith(smil, sourceTracks.toArray(new Track[sourceTracks.size()]));
InputStream is = null;
try {
is = IOUtils.toInputStream(smil.toXML(), "UTF-8");
// Remove old SMIL
workspace.delete(mp.getIdentifier().compact(), smilCatalogs[0].getIdentifier());
mp.remove(smilCatalogs[0]);
// put modified SMIL into workspace
URI newSmilUri = workspace.put(mp.getIdentifier().compact(), smil.getId(), SMIL_FILE_NAME, is);
Catalog catalog = (Catalog) MediaPackageElementBuilderFactory.newInstance().newElementBuilder().elementFromURI(newSmilUri, MediaPackageElement.Type.Catalog, smilCatalogs[0].getFlavor());
catalog.setIdentifier(smil.getId());
mp.add(catalog);
} catch (Exception ex) {
throw new WorkflowOperationException(ex);
} finally {
IOUtils.closeQuietly(is);
}
} catch (NotFoundException ex) {
throw new WorkflowOperationException(format("Failed to get SMIL catalog %s from mediapackage %s.", smilCatalogs[0].getIdentifier(), mp.getIdentifier().compact()), ex);
} catch (IOException ex) {
throw new WorkflowOperationException(format("Can't open SMIL catalog %s from mediapackage %s.", smilCatalogs[0].getIdentifier(), mp.getIdentifier().compact()), ex);
} catch (SmilException ex) {
throw new WorkflowOperationException(ex);
}
if (skipIfNoTrim) {
// We should not modify the SMIL file as we traverse through its elements, so we make a copy and modify it instead
try {
Smil filteredSmil = smilService.fromXml(smil.toXML()).getSmil();
for (SmilMediaObject element : smil.getBody().getMediaElements()) {
// body should contain par elements
if (element.isContainer()) {
SmilMediaContainer container = (SmilMediaContainer) element;
if (SmilMediaContainer.ContainerType.PAR == container.getContainerType()) {
continue;
}
}
filteredSmil = smilService.removeSmilElement(filteredSmil, element.getId()).getSmil();
}
// one that takes the whole video size
switch(filteredSmil.getBody().getMediaElements().size()) {
case 0:
logger.info("Skipping SMIL job generation for mediapackage '{}', " + "because the SMIL does not define any trimming points", mp.getIdentifier());
return skip(workflowInstance, context);
case 1:
// component represents the whole duration or not, therefore we don't bother to try
if (mp.getDuration() < 0)
break;
SmilMediaContainer parElement = (SmilMediaContainer) filteredSmil.getBody().getMediaElements().get(0);
boolean skip = true;
for (SmilMediaObject elementChild : parElement.getElements()) {
if (!elementChild.isContainer()) {
SmilMediaElement media = (SmilMediaElement) elementChild;
// If they don't represent the whole length, then we break --we have a trimming point
if ((media.getClipBeginMS() != 0) || (media.getClipEndMS() != mp.getDuration())) {
skip = false;
break;
}
}
}
if (skip) {
logger.info("Skipping SMIL job generation for mediapackage '{}', " + "because the trimming points in the SMIL correspond " + "to the beginning and the end of the video", mp.getIdentifier());
return skip(workflowInstance, context);
}
break;
default:
break;
}
} catch (MalformedURLException | SmilException | JAXBException | SAXException e) {
logger.warn("Error parsing input SMIL to determine if it has trimpoints. " + "We will assume it does and go on creating jobs.");
}
}
// Create video edit jobs and run them
List<Job> jobs = null;
try {
logger.info("Create processing jobs for SMIL file: {}", smilCatalogs[0].getIdentifier());
jobs = videoEditorService.processSmil(smil);
if (!waitForStatus(jobs.toArray(new Job[jobs.size()])).isSuccess()) {
throw new WorkflowOperationException(format("Processing SMIL file failed: %s", smilCatalogs[0].getIdentifier()));
}
logger.info("Finished processing of SMIL file: {}", smilCatalogs[0].getIdentifier());
} catch (ProcessFailedException ex) {
throw new WorkflowOperationException(format("Finished processing of SMIL file: %s", smilCatalogs[0].getIdentifier()), ex);
}
// Move edited tracks to work location and set target flavor
Track editedTrack = null;
boolean mpAdded = false;
for (Job job : jobs) {
try {
editedTrack = (Track) MediaPackageElementParser.getFromXml(job.getPayload());
MediaPackageElementFlavor editedTrackFlavor = editedTrack.getFlavor();
editedTrack.setFlavor(new MediaPackageElementFlavor(editedTrackFlavor.getType(), targetFlavorSybTypeProperty));
URI editedTrackNewUri = workspace.moveTo(editedTrack.getURI(), mp.getIdentifier().compact(), editedTrack.getIdentifier(), FilenameUtils.getName(editedTrack.getURI().toString()));
editedTrack.setURI(editedTrackNewUri);
for (Track track : sourceTracks) {
if (track.getFlavor().getType().equals(editedTrackFlavor.getType())) {
mp.addDerived(editedTrack, track);
mpAdded = true;
break;
}
}
if (!mpAdded) {
mp.add(editedTrack);
}
} catch (MediaPackageException ex) {
throw new WorkflowOperationException("Failed to get information about the edited track(s)", ex);
} catch (NotFoundException | IOException | IllegalArgumentException ex) {
throw new WorkflowOperationException("Moving edited track to work location failed.", ex);
} catch (Exception ex) {
throw new WorkflowOperationException(ex);
}
}
logger.info("VideoEdit workflow {} finished", workflowInstance.getId());
return createResult(mp, Action.CONTINUE);
}
use of org.opencastproject.workflow.api.WorkflowOperationInstance in project opencast by opencast.
the class LoggingWorkflowOperationHandler method start.
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance,
* JobContext)
*/
@Override
public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException {
WorkflowOperationInstance operation = workflowInstance.getCurrentOperation();
String directoryPath = StringUtils.trimToNull(operation.getConfiguration("directory"));
File directory = null;
if (directoryPath != null) {
directory = new File(directoryPath);
try {
FileUtils.forceMkdir(directory);
} catch (IOException e) {
throw new WorkflowOperationException(String.format("Failed to create output directory '%s'", directoryPath), e);
}
}
boolean logMediaPackageJSON = BooleanUtils.toBoolean(operation.getConfiguration("mediapackage-json"));
boolean logMediaPackageXML = BooleanUtils.toBoolean(operation.getConfiguration("mediapackage-xml"));
boolean logWorkflowInstance = BooleanUtils.toBoolean(operation.getConfiguration("workflowinstance-xml"));
if (!(logMediaPackageJSON || logMediaPackageXML || logWorkflowInstance)) {
logMediaPackageJSON = true;
}
MediaPackage mediaPackage = workflowInstance.getMediaPackage();
// Log media package as JSON
if (logMediaPackageJSON) {
String filename = String.format("workflow-%d-%d-mediapackage-%s.json", workflowInstance.getId(), operation.getId(), mediaPackage.getIdentifier());
saveOrLog(MediaPackageParser.getAsJSON(mediaPackage), directory, filename);
}
// Log media package as XML
if (logMediaPackageXML) {
String filename = String.format("workflow-%d-%d-mediapackage-%s.xml", workflowInstance.getId(), operation.getId(), mediaPackage.getIdentifier());
saveOrLog(MediaPackageParser.getAsXml(mediaPackage), directory, filename);
}
// Log workflow instance as XML
if (logWorkflowInstance) {
String filename = String.format("workflow-%d-%d.xml", workflowInstance.getId(), operation.getId());
try {
saveOrLog(WorkflowParser.toXml(workflowInstance), directory, filename);
} catch (WorkflowParsingException e) {
throw new WorkflowOperationException(e);
}
}
// Continue with unmodified media package
return createResult(mediaPackage, Action.CONTINUE);
}
use of org.opencastproject.workflow.api.WorkflowOperationInstance in project opencast by opencast.
the class InspectWorkflowOperationHandlerTest method getWorkflowOperationResult.
private WorkflowOperationResult getWorkflowOperationResult(MediaPackage mp) throws WorkflowOperationException {
// Add the mediapackage to a workflow instance
WorkflowInstanceImpl workflowInstance = new WorkflowInstanceImpl();
workflowInstance.setId(1);
workflowInstance.setState(WorkflowState.RUNNING);
workflowInstance.setMediaPackage(mp);
WorkflowOperationInstanceImpl operation = new WorkflowOperationInstanceImpl("op", OperationState.RUNNING);
List<WorkflowOperationInstance> operationsList = new ArrayList<WorkflowOperationInstance>();
operationsList.add(operation);
workflowInstance.setOperations(operationsList);
// Run the media package through the operation handler, ensuring that metadata gets added
return operationHandler.start(workflowInstance, null);
}
use of org.opencastproject.workflow.api.WorkflowOperationInstance in project opencast by opencast.
the class ExecuteOnceWorkflowOperationHandlerTest method setUp.
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
// Mocking just about everything, just testing the mediapackage parse
String expectedTypeString = "catalog";
String catalogId = "catalog-id";
URI catUri = new URI("http://api.com/catalog");
catalog = CatalogImpl.newInstance();
catalog.addTag("engage-download");
catalog.setIdentifier(catalogId);
catalog.setURI(catUri);
WorkflowOperationInstance operation = EasyMock.createMock(WorkflowOperationInstance.class);
EasyMock.expect(operation.getId()).andReturn(123L).anyTimes();
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.EXEC_PROPERTY)).andReturn(null).anyTimes();
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.PARAMS_PROPERTY)).andReturn(null).anyTimes();
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.LOAD_PROPERTY)).andReturn("123").anyTimes();
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.TARGET_FLAVOR_PROPERTY)).andReturn(null).anyTimes();
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.TARGET_TAGS_PROPERTY)).andReturn(null).anyTimes();
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.OUTPUT_FILENAME_PROPERTY)).andReturn(null).anyTimes();
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.SET_WF_PROPS_PROPERTY)).andReturn("false").anyTimes();
// these two need to supply a real string
EasyMock.expect(operation.getConfiguration(ExecuteOnceWorkflowOperationHandler.EXPECTED_TYPE_PROPERTY)).andReturn(expectedTypeString).anyTimes();
EasyMock.replay(operation);
Id mpId = EasyMock.createMock(Id.class);
MediaPackage mediaPackage = EasyMock.createMock(MediaPackage.class);
mediaPackage.add((MediaPackageElement) EasyMock.anyObject());
EasyMock.expect(mediaPackage.getIdentifier()).andReturn(mpId).anyTimes();
EasyMock.replay(mediaPackage);
workspaceService = EasyMock.createMock(Workspace.class);
EasyMock.expect(workspaceService.moveTo((URI) EasyMock.anyObject(), (String) EasyMock.anyObject(), (String) EasyMock.anyObject(), (String) EasyMock.anyObject())).andReturn(catUri).anyTimes();
EasyMock.replay(workspaceService);
workflowInstance = EasyMock.createMock(WorkflowInstance.class);
EasyMock.expect(workflowInstance.getMediaPackage()).andReturn(mediaPackage).anyTimes();
EasyMock.expect(workflowInstance.getCurrentOperation()).andStubReturn(operation);
EasyMock.replay(workflowInstance);
// Override the waitForStatus method to not block the jobs
execOnceWOH = new ExecuteOnceWorkflowOperationHandler() {
@Override
protected Result waitForStatus(long timeout, Job... jobs) {
HashMap<Job, Status> map = Stream.mk(jobs).foldl(new HashMap<Job, Status>(), new Fn2<HashMap<Job, Status>, Job, HashMap<Job, Status>>() {
@Override
public HashMap<Job, Status> apply(HashMap<Job, Status> a, Job b) {
a.put(b, Status.FINISHED);
return a;
}
});
return new Result(map);
}
};
execOnceWOH.setWorkspace(workspaceService);
}
use of org.opencastproject.workflow.api.WorkflowOperationInstance in project opencast by opencast.
the class ExecuteOnceWorkflowOperationHandler method start.
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance, JobContext)
*/
@Override
public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException {
MediaPackage mediaPackage = workflowInstance.getMediaPackage();
WorkflowOperationInstance operation = workflowInstance.getCurrentOperation();
logger.debug("Running execute workflow operation with ID {}", operation.getId());
// Get operation parameters
String exec = StringUtils.trimToNull(operation.getConfiguration(EXEC_PROPERTY));
String params = StringUtils.trimToNull(operation.getConfiguration(PARAMS_PROPERTY));
float load = 1.0f;
String loadPropertyStr = StringUtils.trimToEmpty(operation.getConfiguration(LOAD_PROPERTY));
if (StringUtils.isNotBlank(loadPropertyStr)) {
try {
load = Float.parseFloat(loadPropertyStr);
} catch (NumberFormatException e) {
String description = StringUtils.trimToEmpty(operation.getDescription());
logger.warn("Ignoring invalid load value '{}' on execute operation with description '{}'", loadPropertyStr, description);
}
}
String targetFlavorStr = StringUtils.trimToNull(operation.getConfiguration(TARGET_FLAVOR_PROPERTY));
String targetTags = StringUtils.trimToNull(operation.getConfiguration(TARGET_TAGS_PROPERTY));
String outputFilename = StringUtils.trimToNull(operation.getConfiguration(OUTPUT_FILENAME_PROPERTY));
String expectedTypeStr = StringUtils.trimToNull(operation.getConfiguration(EXPECTED_TYPE_PROPERTY));
boolean setWfProps = Boolean.valueOf(StringUtils.trimToNull(operation.getConfiguration(SET_WF_PROPS_PROPERTY)));
// Unmarshall target flavor
MediaPackageElementFlavor targetFlavor = null;
if (targetFlavorStr != null)
targetFlavor = MediaPackageElementFlavor.parseFlavor(targetFlavorStr);
// Unmarshall expected mediapackage element type
MediaPackageElement.Type expectedType = null;
if (expectedTypeStr != null) {
for (MediaPackageElement.Type type : MediaPackageElement.Type.values()) if (type.toString().equalsIgnoreCase(expectedTypeStr)) {
expectedType = type;
break;
}
if (expectedType == null)
throw new WorkflowOperationException("'" + expectedTypeStr + "' is not a valid element type");
}
// Process the result element
MediaPackageElement resultElement = null;
try {
Job job = executeService.execute(exec, params, mediaPackage, outputFilename, expectedType, load);
WorkflowOperationResult result = null;
// Wait for all jobs to be finished
if (!waitForStatus(job).isSuccess())
throw new WorkflowOperationException("Execute operation failed");
if (StringUtils.isNotBlank(job.getPayload())) {
if (setWfProps) {
// The job payload is a file with set of properties for the workflow
resultElement = MediaPackageElementParser.getFromXml(job.getPayload());
final Properties properties = new Properties();
File propertiesFile = workspace.get(resultElement.getURI());
try (InputStream is = new FileInputStream(propertiesFile)) {
properties.load(is);
}
logger.debug("Loaded {} properties from {}", properties.size(), propertiesFile);
workspace.deleteFromCollection(ExecuteService.COLLECTION, propertiesFile.getName());
Map<String, String> wfProps = new HashMap<String, String>((Map) properties);
result = createResult(mediaPackage, wfProps, Action.CONTINUE, job.getQueueTime());
} else {
// The job payload is a new element for the MediaPackage
resultElement = MediaPackageElementParser.getFromXml(job.getPayload());
if (resultElement.getElementType() == MediaPackageElement.Type.Track) {
// Have the track inspected and return the result
Job inspectionJob = null;
inspectionJob = inspectionService.inspect(resultElement.getURI());
JobBarrier barrier = new JobBarrier(job, serviceRegistry, inspectionJob);
if (!barrier.waitForJobs().isSuccess()) {
throw new ExecuteException("Media inspection of " + resultElement.getURI() + " failed");
}
resultElement = MediaPackageElementParser.getFromXml(inspectionJob.getPayload());
}
// Store new element to mediaPackage
mediaPackage.add(resultElement);
URI uri = workspace.moveTo(resultElement.getURI(), mediaPackage.getIdentifier().toString(), resultElement.getIdentifier(), outputFilename);
resultElement.setURI(uri);
// Set new flavor
if (targetFlavor != null)
resultElement.setFlavor(targetFlavor);
// Set new tags
if (targetTags != null) {
// Assume the tags starting with "-" means we want to eliminate such tags form the result element
for (String tag : asList(targetTags)) {
if (tag.startsWith("-"))
// We remove the tag resulting from stripping all the '-' characters at the beginning of the tag
resultElement.removeTag(tag.replaceAll("^-+", ""));
else
resultElement.addTag(tag);
}
}
result = createResult(mediaPackage, Action.CONTINUE, job.getQueueTime());
}
} else {
// Payload is empty
result = createResult(mediaPackage, Action.CONTINUE, job.getQueueTime());
}
logger.debug("Execute operation {} completed", operation.getId());
return result;
} catch (ExecuteException e) {
throw new WorkflowOperationException(e);
} catch (MediaPackageException e) {
throw new WorkflowOperationException("Some result element couldn't be serialized", e);
} catch (NotFoundException e) {
throw new WorkflowOperationException("Could not find mediapackage", e);
} catch (IOException e) {
throw new WorkflowOperationException("Error unmarshalling a result mediapackage element", e);
} catch (MediaInspectionException e) {
throw new WorkflowOperationException("Media inspection of " + resultElement.getURI() + " failed", e);
}
}
Aggregations