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