VideoEditorWorkflowOperationHandler extends ResumableWorkflowOperationHandlerBase { @Override public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException { MediaPackage mp = workflowInstance.getMediaPackage(); logger.info("Start editor workflow for mediapackage {}", mp.getIdentifier().toString()); WorkflowOperationInstance worflowOperationInstance = workflowInstance.getCurrentOperation(); String smilFlavorsProperty = StringUtils .trimToNull(worflowOperationInstance.getConfiguration(SMIL_FLAVORS_PROPERTY)); if (smilFlavorsProperty == null) { throw new WorkflowOperationException(format("Required configuration property %s not set", SMIL_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 previewTrackFlavorsProperty = StringUtils .trimToNull(worflowOperationInstance.getConfiguration(PREVIEW_FLAVORS_PROPERTY)); if (previewTrackFlavorsProperty == null) { logger.info("Configuration property '{}' not set, use preview tracks from SMIL catalog", PREVIEW_FLAVORS_PROPERTY); } final boolean skipProcessing = BooleanUtils .toBoolean(worflowOperationInstance.getConfiguration(SKIP_PROCESSING_PROPERTY)); if (!skipProcessing && StringUtils .trimToNull(worflowOperationInstance.getConfiguration(TARGET_FLAVOR_SUBTYPE_PROPERTY)) == null) { throw new WorkflowOperationException( String.format("Required configuration property %s not set", TARGET_FLAVOR_SUBTYPE_PROPERTY)); } final boolean interactive = BooleanUtils.toBoolean(worflowOperationInstance.getConfiguration(INTERACTIVE_PROPERTY)); SimpleElementSelector elementSelector = new SimpleElementSelector(); for (String flavor : asList(smilFlavorsProperty)) { elementSelector.addFlavor(flavor); } Collection<MediaPackageElement> smilCatalogs = elementSelector.select(mp, false); MediaPackageElementBuilder mpeBuilder = MediaPackageElementBuilderFactory.newInstance().newElementBuilder(); if (smilCatalogs.isEmpty()) { if (!interactive && !skipProcessing) { logger.info("Skipping cutting operation since no edit decision list is available"); return skip(workflowInstance, context); } if (previewTrackFlavorsProperty == null) { throw new WorkflowOperationException( format("No SMIL catalogs with flavor %s nor preview files with flavor %s found in mediapackage %s", smilFlavorsProperty, previewTrackFlavorsProperty, mp.getIdentifier().toString())); } TrackSelector trackSelector = new TrackSelector(); for (String flavor : asList(previewTrackFlavorsProperty)) { trackSelector.addFlavor(flavor); } Collection<Track> previewTracks = trackSelector.select(mp, false); if (previewTracks.isEmpty()) { throw new WorkflowOperationException(format("No preview tracks found in mediapackage %s with flavor %s", mp.getIdentifier().toString(), previewTrackFlavorsProperty)); } Track[] previewTracksArr = previewTracks.toArray(new Track[previewTracks.size()]); MediaPackageElementFlavor smilFlavor = MediaPackageElementFlavor.parseFlavor(smilFlavorsProperty); for (Track previewTrack : previewTracks) { try { SmilResponse smilResponse = smilService.createNewSmil(mp); smilResponse = smilService.addParallel(smilResponse.getSmil()); smilResponse = smilService.addClips(smilResponse.getSmil(), smilResponse.getEntity().getId(), previewTracksArr, 0L, previewTracksArr[0].getDuration()); Smil smil = smilResponse.getSmil(); InputStream is = null; try { is = IOUtils.toInputStream(smil.toXML(), "UTF-8"); URI smilURI = workspace.put(mp.getIdentifier().toString(), smil.getId(), SMIL_FILE_NAME, is); MediaPackageElementFlavor trackSmilFlavor = previewTrack.getFlavor(); if (!"*".equals(smilFlavor.getType())) { trackSmilFlavor = new MediaPackageElementFlavor(smilFlavor.getType(), trackSmilFlavor.getSubtype()); } if (!"*".equals(smilFlavor.getSubtype())) { trackSmilFlavor = new MediaPackageElementFlavor(trackSmilFlavor.getType(), smilFlavor.getSubtype()); } Catalog catalog = (Catalog) mpeBuilder.elementFromURI(smilURI, MediaPackageElement.Type.Catalog, trackSmilFlavor); catalog.setIdentifier(smil.getId()); mp.add(catalog); } finally { IOUtils.closeQuietly(is); } } catch (Exception ex) { throw new WorkflowOperationException( format("Failed to create SMIL catalog for mediapackage %s", mp.getIdentifier().toString()), ex); } } } MediaPackageElementFlavor targetSmilFlavor = MediaPackageElementFlavor.parseFlavor(targetSmilFlavorProperty); Catalog[] targetSmilCatalogs = mp.getCatalogs(targetSmilFlavor); if (targetSmilCatalogs == null || targetSmilCatalogs.length == 0) { if (!interactive && !skipProcessing) return skip(workflowInstance, context); try { SmilResponse smilResponse = smilService.createNewSmil(mp); Smil smil = smilResponse.getSmil(); InputStream is = null; try { is = IOUtils.toInputStream(smil.toXML(), "UTF-8"); URI smilURI = workspace.put(mp.getIdentifier().toString(), smil.getId(), SMIL_FILE_NAME, is); Catalog catalog = (Catalog) mpeBuilder.elementFromURI(smilURI, MediaPackageElement.Type.Catalog, targetSmilFlavor); catalog.setIdentifier(smil.getId()); mp.add(catalog); } finally { IOUtils.closeQuietly(is); } } catch (Exception ex) { throw new WorkflowOperationException( format("Failed to create an initial empty SMIL catalog for mediapackage %s", mp.getIdentifier().toString()), ex); } if (!interactive) return skip(workflowInstance, context); logger.info("Holding for video edit..."); return createResult(mp, Action.PAUSE); } else { logger.debug("Move on, SMIL catalog ({}) already exists for media package '{}'", targetSmilFlavor, mp); return resume(workflowInstance, context, Collections.<String, String> emptyMap()); } }  @Override void activate(ComponentContext cc); @Override WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context); @Override WorkflowOperationResult skip(WorkflowInstance workflowInstance, JobContext context); @Override WorkflowOperationResult resume(WorkflowInstance workflowInstance, JobContext context,
          Map<String, String> properties); void setSmilService(SmilService smilService); void setVideoEditorService(VideoEditorService editor); void setWorkspace(Workspace workspace);  }
@Test public void testEditorOperationStart() throws WorkflowOperationException, IOException { EasyMock.expect( workspaceMock.put((String) EasyMock.anyObject(), (String) EasyMock.anyObject(), (String) EasyMock.anyObject(), (InputStream) EasyMock.anyObject())).andReturn( URI.create("http: String episodeSmilUri = "http: EasyMock.expect( workspaceMock.put((String) EasyMock.anyObject(), (String) EasyMock.anyObject(), (String) EasyMock.anyObject(), (InputStream) EasyMock.anyObject())).andReturn( URI.create(episodeSmilUri)); EasyMock.replay(workspaceMock); WorkflowInstanceImpl workflowInstance = getWorkflowInstance(mp, getDefaultConfiguration(true)); WorkflowOperationResult result = videoEditorWorkflowOperationHandler.start(workflowInstance, null); Assert.assertNotNull( "VideoEditor workflow operation returns null but should be an instantiated WorkflowOperationResult", result); EasyMock.verify(workspaceMock); WorkflowOperationInstance worflowOperationInstance = workflowInstance.getCurrentOperation(); String smillFlavorsProperty = worflowOperationInstance.getConfiguration("smil-flavors"); String previewFlavorsProperty = worflowOperationInstance.getConfiguration("preview-flavors"); MediaPackageElementFlavor smilFlavor = MediaPackageElementFlavor.parseFlavor(smillFlavorsProperty); MediaPackageElementFlavor previewFlavor = MediaPackageElementFlavor.parseFlavor(previewFlavorsProperty); Catalog[] previewSmilCatalogs = result.getMediaPackage().getCatalogs( new MediaPackageElementFlavor("presenter", "smil")); Assert.assertTrue(previewSmilCatalogs != null && previewSmilCatalogs.length > 0); for (Track track : result.getMediaPackage().getTracks()) { if (track.getFlavor().matches(previewFlavor)) { boolean smilCatalogFound = false; MediaPackageElementFlavor trackSmilFlavor = new MediaPackageElementFlavor(track.getFlavor().getType(), smilFlavor.getSubtype()); for (Catalog previewSmilCatalog : previewSmilCatalogs) { if (previewSmilCatalog.getFlavor().matches(trackSmilFlavor)) { smilCatalogFound = true; break; } } Assert.assertTrue("Mediapackage doesn't contain a smil catalog with flavor " + trackSmilFlavor.toString(), smilCatalogFound); } } String targetSmilFlavorProperty = worflowOperationInstance.getConfiguration("target-smil-flavor"); Catalog[] episodeSmilCatalogs = result.getMediaPackage().getCatalogs( MediaPackageElementFlavor.parseFlavor(targetSmilFlavorProperty)); Assert.assertTrue("Mediapackage should contain catalog with flavor " + targetSmilFlavorProperty, episodeSmilCatalogs != null && episodeSmilCatalogs.length > 0); Assert.assertTrue("Target smil catalog URI does not match", episodeSmilCatalogs[0].getURI().compareTo(URI.create(episodeSmilUri)) == 0); }
@Test public void testEditorOperationInteractiveSkip() throws WorkflowOperationException { WorkflowInstanceImpl workflowInstance = getWorkflowInstance(mp, getDefaultConfiguration(false)); WorkflowOperationResult result = videoEditorWorkflowOperationHandler.start(workflowInstance, null); Assert.assertNotNull( "VideoEditor workflow operation returns null but should be an instantiated WorkflowOperationResult", result); WorkflowOperationInstance worflowOperationInstance = workflowInstance.getCurrentOperation(); String targetFlavorSubtypeProperty = worflowOperationInstance.getConfiguration("target-flavor-subtype"); String skippedFlavorsProperty = worflowOperationInstance.getConfiguration("skipped-flavors"); TrackSelector trackSelector = new TrackSelector(); trackSelector.addFlavor(skippedFlavorsProperty); Collection<Track> skippedTracks = trackSelector.select(result.getMediaPackage(), false); Assert.assertTrue("Mediapackage does not contain any tracks matching flavor " + skippedFlavorsProperty, skippedTracks != null && !skippedTracks.isEmpty()); for (Track skippedTrack : skippedTracks) { MediaPackageElementFlavor derivedTrackFlavor = MediaPackageElementFlavor.flavor(skippedTrack.getFlavor() .getType(), targetFlavorSubtypeProperty); MediaPackageElement[] derivedElements = result.getMediaPackage().getDerived(skippedTrack, derivedTrackFlavor); Assert.assertTrue("Media package should contain track with flavor " + derivedTrackFlavor.toString(), derivedElements != null && derivedElements.length > 0); } }