View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  package org.opencastproject.transcription.workflowoperation;
22  
23  import org.opencastproject.job.api.Job;
24  import org.opencastproject.job.api.JobContext;
25  import org.opencastproject.mediapackage.MediaPackage;
26  import org.opencastproject.mediapackage.MediaPackageElement;
27  import org.opencastproject.mediapackage.MediaPackageElementFlavor;
28  import org.opencastproject.mediapackage.Track;
29  import org.opencastproject.mediapackage.selector.AbstractMediaPackageElementSelector;
30  import org.opencastproject.mediapackage.selector.TrackSelector;
31  import org.opencastproject.serviceregistry.api.ServiceRegistry;
32  import org.opencastproject.transcription.api.TranscriptionService;
33  import org.opencastproject.transcription.api.TranscriptionServiceException;
34  import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
35  import org.opencastproject.workflow.api.ConfiguredTagsAndFlavors;
36  import org.opencastproject.workflow.api.WorkflowInstance;
37  import org.opencastproject.workflow.api.WorkflowOperationException;
38  import org.opencastproject.workflow.api.WorkflowOperationHandler;
39  import org.opencastproject.workflow.api.WorkflowOperationInstance;
40  import org.opencastproject.workflow.api.WorkflowOperationResult;
41  import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
42  
43  import org.apache.commons.lang3.StringUtils;
44  import org.osgi.service.component.ComponentContext;
45  import org.osgi.service.component.annotations.Activate;
46  import org.osgi.service.component.annotations.Component;
47  import org.osgi.service.component.annotations.Reference;
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  import java.util.Collection;
52  import java.util.List;
53  
54  @Component(
55      immediate = true,
56      service = WorkflowOperationHandler.class,
57      property = {
58          "service.description=Start Transcription Workflow Operation Handler",
59          "workflow.operation=start-watson-transcription"
60      }
61  )
62  public class StartTranscriptionOperationHandler extends AbstractWorkflowOperationHandler {
63  
64    /** The logging facility */
65    private static final Logger logger = LoggerFactory.getLogger(StartTranscriptionOperationHandler.class);
66  
67    /** Workflow configuration option keys */
68    static final String SKIP_IF_FLAVOR_EXISTS = "skip-if-flavor-exists";
69  
70    /** The transcription service */
71    private TranscriptionService service = null;
72  
73    @Override
74    @Activate
75    protected void activate(ComponentContext cc) {
76      super.activate(cc);
77    }
78  
79    @Override
80    public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context)
81            throws WorkflowOperationException {
82      MediaPackage mediaPackage = workflowInstance.getMediaPackage();
83      WorkflowOperationInstance operation = workflowInstance.getCurrentOperation();
84  
85      String skipOption = StringUtils.trimToNull(operation.getConfiguration(SKIP_IF_FLAVOR_EXISTS));
86      if (skipOption != null) {
87        MediaPackageElement[] mpes = mediaPackage.getElementsByFlavor(MediaPackageElementFlavor.parseFlavor(skipOption));
88        if (mpes != null && mpes.length > 0) {
89          logger.info(
90                  "Start transcription operation will be skipped because flavor {} already exists in the media package",
91                  skipOption);
92          return createResult(Action.SKIP);
93        }
94      }
95  
96      logger.debug("Start transcription for mediapackage {} started", mediaPackage);
97  
98      // Check which tags have been configured
99      ConfiguredTagsAndFlavors tagsAndFlavors = getTagsAndFlavors(
100         workflowInstance, Configuration.many, Configuration.many, Configuration.none, Configuration.none);
101     List<String> sourceTagOption = tagsAndFlavors.getSrcTags();
102     List<MediaPackageElementFlavor> sourceFlavorOption = tagsAndFlavors.getSrcFlavors();
103 
104     AbstractMediaPackageElementSelector<Track> elementSelector = new TrackSelector();
105 
106     // Make sure either one of tags or flavors are provided
107     if (sourceTagOption.isEmpty() && sourceFlavorOption.isEmpty()) {
108       throw new WorkflowOperationException("No source tag or flavor have been specified!");
109     }
110 
111     if (!sourceFlavorOption.isEmpty()) {
112       MediaPackageElementFlavor flavor = sourceFlavorOption.get(0);
113       elementSelector.addFlavor(flavor);
114     }
115     if (!sourceTagOption.isEmpty()) {
116       elementSelector.addTag(sourceTagOption.get(0));
117     }
118 
119     Collection<Track> elements = elementSelector.select(mediaPackage, false);
120     Job job = null;
121     for (Track track : elements) {
122       if (track.hasVideo()) {
123         logger.info("Skipping track {} since it contains a video stream", track);
124         continue;
125       }
126       if (!track.hasAudio()) {
127         logger.info("Track {} from media package {} doesn't contain audio stream. Skip subtitle generation.",
128             track.getFlavor(), mediaPackage.getIdentifier());
129         continue;
130       }
131       try {
132         job = service.startTranscription(mediaPackage.getIdentifier().toString(), track);
133         // Only one job per media package
134         break;
135       } catch (TranscriptionServiceException e) {
136         throw new WorkflowOperationException(e);
137       }
138     }
139 
140     if (job == null) {
141       logger.info("No matching tracks found");
142       return createResult(mediaPackage, Action.CONTINUE);
143     }
144 
145     // Wait for the jobs to return
146     if (!waitForStatus(job).isSuccess()) {
147       throw new WorkflowOperationException("Transcription job did not complete successfully");
148     }
149     // Return OK means that the ibm watson job was created, but not finished yet
150 
151     logger.debug("External transcription job for mediapackage {} was created", mediaPackage);
152 
153     // Results are empty, we should get a callback when transcription is done
154     return createResult(Action.CONTINUE);
155   }
156 
157   @Reference(target = "(provider=ibm.watson)")
158   public void setTranscriptionService(TranscriptionService service) {
159     this.service = service;
160   }
161 
162   @Reference
163   @Override
164   public void setServiceRegistry(ServiceRegistry serviceRegistry) {
165     super.setServiceRegistry(serviceRegistry);
166   }
167 
168 }