1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.workflow.handler.composer;
23
24 import org.opencastproject.composer.api.EncoderException;
25 import org.opencastproject.job.api.JobContext;
26 import org.opencastproject.mediapackage.AdaptivePlaylist;
27 import org.opencastproject.mediapackage.AdaptivePlaylist.HLSMediaPackageCheck;
28 import org.opencastproject.mediapackage.MediaPackage;
29 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
30 import org.opencastproject.mediapackage.MediaPackageException;
31 import org.opencastproject.mediapackage.Track;
32 import org.opencastproject.mediapackage.selector.TrackSelector;
33 import org.opencastproject.serviceregistry.api.ServiceRegistry;
34 import org.opencastproject.util.NotFoundException;
35 import org.opencastproject.util.data.Function2;
36 import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
37 import org.opencastproject.workflow.api.ConfiguredTagsAndFlavors;
38 import org.opencastproject.workflow.api.WorkflowInstance;
39 import org.opencastproject.workflow.api.WorkflowOperationException;
40 import org.opencastproject.workflow.api.WorkflowOperationHandler;
41 import org.opencastproject.workflow.api.WorkflowOperationInstance;
42 import org.opencastproject.workflow.api.WorkflowOperationResult;
43 import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
44 import org.opencastproject.workspace.api.Workspace;
45
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.io.File;
52 import java.io.FileInputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.net.URI;
56 import java.net.URISyntaxException;
57 import java.util.ArrayList;
58 import java.util.Collection;
59 import java.util.List;
60 import java.util.function.Function;
61
62
63
64
65
66 @Component(
67 immediate = true,
68 service = WorkflowOperationHandler.class,
69 property = {
70 "service.description=Sanitize Adaptive Workflow Operation Handler",
71 "workflow.operation=sanitize-adaptive"
72 }
73 )
74 public class SanitizeAdaptiveWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
75
76
77 private static final Logger logger = LoggerFactory.getLogger(SanitizeAdaptiveWorkflowOperationHandler.class);
78 private static final String PLUS = "+";
79 private static final String MINUS = "-";
80
81
82 private Workspace workspace = null;
83
84
85
86
87
88
89
90
91 @Reference
92 public void setWorkspace(Workspace workspace) {
93 this.workspace = workspace;
94 }
95
96 @Reference
97 @Override
98 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
99 super.setServiceRegistry(serviceRegistry);
100 }
101
102
103
104
105
106
107
108 @Override
109 public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context)
110 throws WorkflowOperationException {
111 logger.debug("Running HLS Check workflow operation on workflow {}", workflowInstance.getId());
112 try {
113 return sanitizeHLS(workflowInstance);
114 } catch (Exception e) {
115 throw new WorkflowOperationException(e);
116 }
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 private WorkflowOperationResult sanitizeHLS(WorkflowInstance wi)
135 throws EncoderException,
136 WorkflowOperationException, NotFoundException, MediaPackageException, IOException, URISyntaxException {
137 MediaPackage src = wi.getMediaPackage();
138 MediaPackage mediaPackage = (MediaPackage) src.clone();
139
140 WorkflowOperationInstance operation = wi.getCurrentOperation();
141
142
143 ConfiguredTagsAndFlavors tagsAndFlavors = getTagsAndFlavors(wi,
144 Configuration.none, Configuration.one, Configuration.many, Configuration.one);
145
146
147 MediaPackageElementFlavor sourceFlavor = tagsAndFlavors.getSingleSrcFlavor();
148 List<String> targetTrackTags = tagsAndFlavors.getTargetTags();
149 MediaPackageElementFlavor targetFlavor = tagsAndFlavors.getSingleTargetFlavor();
150
151 List<String> removeTags = new ArrayList<String>();
152 List<String> addTags = new ArrayList<String>();
153 List<String> overrideTags = new ArrayList<String>();
154
155 if (!targetTrackTags.isEmpty()) {
156 for (String tag : targetTrackTags) {
157 if (tag.startsWith(MINUS)) {
158 removeTags.add(tag);
159 } else if (tag.startsWith(PLUS)) {
160 addTags.add(tag);
161 } else {
162 overrideTags.add(tag);
163 }
164 }
165 }
166
167
168 TrackSelector trackSelector = new TrackSelector();
169 trackSelector.addFlavor(sourceFlavor);
170 Collection<Track> tracks = trackSelector.select(mediaPackage, false);
171 List<Track> tracklist = new ArrayList<>(tracks);
172
173
174 if (!tracklist.stream().filter(AdaptivePlaylist.isHLSTrackPred).findAny().isPresent()) {
175 return createResult(mediaPackage, Action.CONTINUE, 0);
176 }
177 HLSMediaPackageCheck hlstree;
178 try {
179 hlstree = new HLSMediaPackageCheck(tracklist, new Function<URI, File>() {
180 @Override
181 public File apply(URI uri) {
182 try {
183 return workspace.get(uri);
184 } catch (NotFoundException | IOException e1) {
185 logger.error("Cannot get {} from workspace", uri, e1);
186 }
187 return null;
188 }
189 });
190 } catch (URISyntaxException e1) {
191 throw new MediaPackageException("Cannot process tracks from workspace");
192 }
193
194
195
196
197 Function2<File, Track, Track> replaceHLSPlaylistInWS = new Function2<File, Track, Track>() {
198 @Override
199 public Track apply(File file, Track track) {
200 try {
201 InputStream inputStream = new FileInputStream(file);
202
203 URI uri = workspace.put(mediaPackage.getIdentifier().toString(), track.getIdentifier(), file.getName(),
204 inputStream);
205 track.setURI(uri);
206 handleTags(track, targetFlavor, overrideTags, removeTags, addTags);
207 return track;
208 } catch (Exception e) {
209 logger.error("Cannot add track file to mediapackage in workspace: {} {} ",
210 mediaPackage.getIdentifier().toString(),
211 file);
212 return null;
213 }
214 }
215 };
216
217 Function<Track, Void> removeFromWS = new Function<Track, Void>() {
218 @Override
219 public Void apply(Track track) {
220 try {
221 workspace.delete(track.getURI());
222 } catch (NotFoundException e) {
223 logger.error("Cannot delete from workspace: File not found {} ", track);
224 } catch (IOException e) {
225 logger.error("Cannot delete from workspace: IO Error {} ", track);
226 }
227 return null;
228 }
229 };
230 if (hlstree.needsRewriting()) {
231
232 try {
233 hlstree.rewriteHLS(mediaPackage, replaceHLSPlaylistInWS, removeFromWS);
234 } catch (Exception e) {
235 logger.error("Error: cannot rewrite HLS renditions", e);
236 throw new WorkflowOperationException(e);
237 }
238 for (Track track : tracks) {
239 if (!AdaptivePlaylist.isPlaylist(track.getURI().getPath())) {
240 handleTags(track, targetFlavor, overrideTags, removeTags, addTags);
241 logger.info("Set flavor {} and tags to {} ", track, targetFlavor);
242 }
243 }
244 } else {
245 for (Track track : tracks) {
246 handleTags(track, targetFlavor, overrideTags, removeTags, addTags);
247 logger.info("Set flavor {} and tags to {} ", track, targetFlavor);
248 }
249 }
250 return createResult(mediaPackage, Action.CONTINUE, 0);
251 }
252
253
254 private void handleTags(Track track, MediaPackageElementFlavor targetFlavor, List<String> overrideTags,
255 List<String> removeTags, List<String> addTags) {
256 if (targetFlavor != null) {
257 String flavorType = targetFlavor.getType();
258 String flavorSubtype = targetFlavor.getSubtype();
259 if ("*".equals(flavorType))
260 flavorType = track.getFlavor().getType();
261 if ("*".equals(flavorSubtype))
262 flavorSubtype = track.getFlavor().getSubtype();
263 track.setFlavor(new MediaPackageElementFlavor(flavorType, flavorSubtype));
264 logger.debug("Composed track has flavor '{}'", track.getFlavor());
265 }
266 if (overrideTags.size() > 0) {
267 track.clearTags();
268 for (String tag : overrideTags) {
269 logger.trace("Tagging composed track with '{}'", tag);
270 track.addTag(tag);
271 }
272 } else {
273 for (String tag : removeTags) {
274 logger.trace("Remove tagging '{}' from composed track", tag);
275 track.removeTag(tag.substring(MINUS.length()));
276 }
277 for (String tag : addTags) {
278 logger.trace("Add tagging '{}' to composed track", tag);
279 track.addTag(tag.substring(PLUS.length()));
280 }
281 }
282 }
283 }