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.caption.api.CaptionService;
24  import org.opencastproject.job.api.Job;
25  import org.opencastproject.job.api.JobContext;
26  import org.opencastproject.mediapackage.Attachment;
27  import org.opencastproject.mediapackage.MediaPackage;
28  import org.opencastproject.mediapackage.MediaPackageElement;
29  import org.opencastproject.mediapackage.MediaPackageElementFlavor;
30  import org.opencastproject.mediapackage.MediaPackageElementParser;
31  import org.opencastproject.mediapackage.Track;
32  import org.opencastproject.serviceregistry.api.ServiceRegistry;
33  import org.opencastproject.transcription.api.TranscriptionService;
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  import org.opencastproject.workspace.api.Workspace;
43  
44  import org.apache.commons.lang3.StringUtils;
45  import org.osgi.service.component.ComponentContext;
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.List;
52  
53  @Component(
54      immediate = true,
55      service = WorkflowOperationHandler.class,
56      property = {
57          "service.description=Attach Google Speech Transcription Workflow Operation Handler",
58          "workflow.operation=google-speech-attach-transcription"
59      }
60  )
61  public class GoogleSpeechAttachTranscriptionOperationHandler extends AbstractWorkflowOperationHandler {
62  
63    /**
64     * The logging facility
65     */
66    private static final Logger logger = LoggerFactory.getLogger(GoogleSpeechAttachTranscriptionOperationHandler.class);
67  
68    /**
69     * Workflow configuration option keys
70     */
71    static final String TRANSCRIPTION_JOB_ID = "transcription-job-id";
72    static final String TARGET_CAPTION_FORMAT = "target-caption-format";
73    static final String TRANSCRIPTION_LINE_SIZE = "line-size";
74    static final String DEFAULT_LINE_SIZE = "100";
75    static final String TARGET_TYPE = "target-element-type";
76  
77    /**
78     * The transcription service
79     */
80    private TranscriptionService service = null;
81    private Workspace workspace;
82    private CaptionService captionService;
83  
84    @Override
85    protected void activate(ComponentContext cc) {
86      super.activate(cc);
87    }
88  
89    /**
90     * {@inheritDoc}
91     *
92     * @see
93     * org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance,
94     * JobContext)
95     */
96    @Override
97    public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context)
98            throws WorkflowOperationException {
99      MediaPackage mediaPackage = workflowInstance.getMediaPackage();
100     WorkflowOperationInstance operation = workflowInstance.getCurrentOperation();
101 
102     logger.debug("Attach transcription for mediapackage {} started", mediaPackage);
103 
104     // Get job id.
105     String jobId = StringUtils.trimToNull(operation.getConfiguration(TRANSCRIPTION_JOB_ID));
106     if (StringUtils.isBlank(jobId)) {
107       throw new WorkflowOperationException(TRANSCRIPTION_JOB_ID + " missing");
108     }
109 
110     // Check which tags/flavors have been configured
111     ConfiguredTagsAndFlavors tagsAndFlavors = getTagsAndFlavors(
112         workflowInstance, Configuration.none, Configuration.none, Configuration.many, Configuration.one);
113     List<String> targetTagOption = tagsAndFlavors.getTargetTags();
114     // Target flavor is mandatory
115     MediaPackageElementFlavor targetFlavor = tagsAndFlavors.getSingleTargetFlavor();
116 
117     String captionFormatOption = StringUtils.trimToNull(operation.getConfiguration(TARGET_CAPTION_FORMAT));
118 
119     String typeUnparsed = StringUtils.trimToEmpty(operation.getConfiguration(TARGET_TYPE));
120     MediaPackageElement.Type type = null;
121     if (!typeUnparsed.isEmpty()) {
122       // Case insensitive matching between user input (workflow config key) and enum value
123       for (MediaPackageElement.Type t : MediaPackageElement.Type.values()) {
124         if (t.name().equalsIgnoreCase(typeUnparsed)) {
125           type = t;
126         }
127       }
128       if (type == null || (type != Track.TYPE && type != Attachment.TYPE)) {
129         throw new IllegalArgumentException(String.format("The given type '%s' for mediapackage %s was illegal. Please"
130                 + "check the operations' configuration keys.", type, mediaPackage.getIdentifier()));
131       }
132     } else {
133       type = Track.TYPE;
134     }
135 
136     // Get line size if set
137     String lineSize = StringUtils.trimToNull(operation.getConfiguration(TRANSCRIPTION_LINE_SIZE));
138     if (StringUtils.isBlank(lineSize)) {
139       lineSize = DEFAULT_LINE_SIZE; // Use default line size
140     }
141 
142     try {
143       // Get transcription file from the service
144       MediaPackageElement original = service.getGeneratedTranscription(mediaPackage.getIdentifier().toString(), jobId,
145               type);
146       MediaPackageElement transcription = original;
147 
148       // If caption format passed, convert to desired format
149       if (StringUtils.isNotBlank(captionFormatOption)) {
150         Job job = captionService.convert(transcription, "google-speech", captionFormatOption, lineSize);
151         if (!waitForStatus(job).isSuccess()) {
152           throw new WorkflowOperationException("Transcription format conversion job did not complete successfully");
153         }
154         transcription = MediaPackageElementParser.getFromXml(job.getPayload());
155       }
156 
157       // Set the target flavor
158       transcription.setFlavor(targetFlavor);
159 
160       // Add tags
161       for (String tag : targetTagOption) {
162         transcription.addTag(tag);
163       }
164 
165       // Add to media package
166       mediaPackage.add(transcription);
167 
168       String uri = transcription.getURI().toString();
169       String ext = uri.substring(uri.lastIndexOf("."));
170       transcription.setURI(workspace.moveTo(transcription.getURI(), mediaPackage.getIdentifier().toString(),
171               transcription.getIdentifier(), "captions." + ext));
172     } catch (Exception e) {
173       throw new WorkflowOperationException(e);
174     }
175 
176     return createResult(mediaPackage, Action.CONTINUE);
177   }
178 
179   @Reference(target = "(provider=google.speech)")
180   public void setTranscriptionService(TranscriptionService service) {
181     this.service = service;
182   }
183 
184   @Reference
185   public void setWorkspace(Workspace service) {
186     this.workspace = service;
187   }
188 
189   @Reference
190   public void setCaptionService(CaptionService service) {
191     this.captionService = service;
192   }
193 
194   @Reference
195   @Override
196   public void setServiceRegistry(ServiceRegistry serviceRegistry) {
197     super.setServiceRegistry(serviceRegistry);
198   }
199 
200 }