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.rename;
23
24 import org.opencastproject.job.api.JobContext;
25 import org.opencastproject.mediapackage.MediaPackage;
26 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
27 import org.opencastproject.mediapackage.MediaPackageElements;
28 import org.opencastproject.mediapackage.Track;
29 import org.opencastproject.mediapackage.VideoStream;
30 import org.opencastproject.mediapackage.selector.TrackSelector;
31 import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
32 import org.opencastproject.metadata.dublincore.DublinCoreUtil;
33 import org.opencastproject.util.NotFoundException;
34 import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
35 import org.opencastproject.workflow.api.WorkflowInstance;
36 import org.opencastproject.workflow.api.WorkflowOperationException;
37 import org.opencastproject.workflow.api.WorkflowOperationHandler;
38 import org.opencastproject.workflow.api.WorkflowOperationResult;
39 import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
40 import org.opencastproject.workspace.api.Workspace;
41
42 import org.apache.commons.io.FilenameUtils;
43 import org.osgi.service.component.ComponentContext;
44 import org.osgi.service.component.annotations.Activate;
45 import org.osgi.service.component.annotations.Component;
46 import org.osgi.service.component.annotations.Reference;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import java.io.IOException;
51 import java.util.Arrays;
52 import java.util.HashMap;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.UUID;
56
57
58
59
60
61 @Component(
62 property = {
63 "service.description=Rename Files Workflow Operation Handler",
64 "workflow.operation=rename-files"
65 },
66 immediate = true,
67 service = WorkflowOperationHandler.class
68 )
69 public class RenameFilesWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
70
71
72 private static final Logger logger = LoggerFactory.getLogger(RenameFilesWorkflowOperationHandler.class);
73
74
75 private Workspace workspace = null;
76
77
78
79
80
81
82
83
84 @Reference
85 public void setWorkspace(Workspace workspace) {
86 this.workspace = workspace;
87 }
88
89
90
91
92
93
94
95 @Override
96 public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context)
97 throws WorkflowOperationException {
98
99 final var operation = workflowInstance.getCurrentOperation();
100 final var mediaPackage = workflowInstance.getMediaPackage();
101 final var mediaPackageId = mediaPackage.getIdentifier().toString();
102
103 logger.info("Running rename files workflow operation on workflow {}", workflowInstance.getId());
104
105
106
107 String pattern = operation.getConfiguration("name-pattern");
108 if (pattern == null) {
109 throw new WorkflowOperationException("name-pattern must be configured");
110 }
111 logger.debug("name-pattern {}", pattern);
112
113
114 var tagsAndFlavors = getTagsAndFlavors(
115 workflowInstance,
116 Configuration.none,
117 Configuration.many,
118 Configuration.none,
119 Configuration.none);
120
121
122 List<MediaPackageElementFlavor> sourceFlavors = tagsAndFlavors.getSrcFlavors();
123
124 TrackSelector trackSelector = new TrackSelector();
125 for (MediaPackageElementFlavor sourceFlavor: sourceFlavors) {
126 trackSelector.addFlavor(sourceFlavor);
127 }
128
129 for (var track: trackSelector.select(mediaPackage, false)) {
130 var uri = track.getURI();
131 var extension = FilenameUtils.getExtension(uri.toString());
132 var newElementId = UUID.randomUUID().toString();
133
134
135 var filename = pattern;
136 for (var entry: placeholders(mediaPackage, track).entrySet()) {
137 filename = filename.replace(entry.getKey(), entry.getValue());
138 }
139 filename = filename.replaceAll("#\\{[a-z.]*}", "_");
140
141
142
143 try (var in = workspace.read(uri)) {
144 var newUri = workspace.put(mediaPackageId, newElementId, filename, in);
145 logger.info("Renaming {} to {}", uri, newUri);
146 track.setIdentifier(newElementId);
147 track.setURI(newUri);
148 } catch (NotFoundException | IOException e) {
149 throw new WorkflowOperationException("Failed moving track file", e);
150 }
151
152
153 logger.debug("Removing old track file {}", uri);
154 try {
155 workspace.delete(uri);
156 } catch (NotFoundException | IOException e) {
157 logger.debug("Could not remove track from workspace. Could be it was never there.");
158 }
159
160 }
161
162 return createResult(mediaPackage, Action.CONTINUE);
163 }
164
165
166
167
168
169
170
171
172
173
174 private Map<String, String> placeholders(MediaPackage mediaPackage, Track element) {
175
176 var placeholders = new HashMap<String, String>();
177
178 var width = Arrays.stream(element.getStreams())
179 .filter(s -> s instanceof VideoStream)
180 .map(s -> (VideoStream) s)
181 .findFirst()
182 .map(VideoStream::getFrameWidth)
183 .map(Object::toString)
184 .orElse("");
185
186 placeholders.put("#{video.width}", width);
187
188 var height = Arrays.stream(element.getStreams())
189 .filter(h -> h instanceof VideoStream)
190 .map(h -> (VideoStream) h)
191 .findFirst()
192 .map(VideoStream::getFrameHeight)
193 .map(Object::toString)
194 .orElse("");
195
196 placeholders.put("#{video.height}", height);
197
198
199 placeholders.put("#{file.extension}", FilenameUtils.getExtension(element.getURI().toString()));
200 placeholders.put("#{file.basename}", FilenameUtils.getBaseName(element.getURI().toString()));
201
202
203 placeholders.put("#{flavor.type}", element.getFlavor().getType());
204 placeholders.put("#{flavor.subtype}", element.getFlavor().getSubtype());
205
206
207 for (var flavor: Arrays.asList(MediaPackageElements.EPISODE, MediaPackageElements.SERIES)) {
208
209 for (var catalog : mediaPackage.getCatalogs(flavor)) {
210 DublinCoreCatalog dc = DublinCoreUtil.loadDublinCore(workspace, catalog);
211 for (var entry : dc.getValues().entrySet()) {
212 var key = String.format("#{%s.%s}", flavor.getSubtype(), entry.getKey().getLocalName());
213 var value = entry.getValue().get(0).getValue();
214 placeholders.put(key, value);
215 }
216 }
217 }
218
219 logger.debug("Placeholders to use for renaming: {}", placeholders);
220 return placeholders;
221 }
222
223 @Activate
224 @Override
225 protected void activate(ComponentContext cc) {
226 super.activate(cc);
227 }
228 }