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.mediapackage;
23
24 import static org.apache.commons.lang3.StringUtils.isNotBlank;
25 import static org.opencastproject.util.IoSupport.withResource;
26 import static org.opencastproject.util.data.Collections.list;
27 import static org.opencastproject.util.data.functions.Booleans.not;
28 import static org.opencastproject.util.data.functions.Options.sequenceOpt;
29 import static org.opencastproject.util.data.functions.Options.toOption;
30
31 import org.opencastproject.util.data.Function;
32 import org.opencastproject.util.data.Option;
33
34 import com.entwinemedia.fn.data.Opt;
35 import com.entwinemedia.fn.fns.Strings;
36
37 import org.apache.commons.io.FilenameUtils;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 import java.io.InputStream;
42 import java.net.URI;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.function.Consumer;
46
47
48 public final class MediaPackageSupport {
49
50 private MediaPackageSupport() {
51 }
52
53 private static final List NIL = java.util.Collections.EMPTY_LIST;
54
55
56
57
58
59
60
61
62
63
64
65 public enum MergeMode {
66 Merge, Replace, Skip, Fail
67 }
68
69
70 private static final Logger logger = LoggerFactory.getLogger(MediaPackageSupport.class.getName());
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public static MediaPackage merge(MediaPackage dest, MediaPackage src, MergeMode mode) throws MediaPackageException {
90 try {
91 for (MediaPackageElement e : src.elements()) {
92 if (dest.getElementById(e.getIdentifier()) == null)
93 dest.add(e);
94 else {
95 if (MergeMode.Replace == mode) {
96 logger.debug("Replacing element " + e.getIdentifier() + " while merging " + dest + " with " + src);
97 dest.remove(dest.getElementById(e.getIdentifier()));
98 dest.add(e);
99 } else if (MergeMode.Skip == mode) {
100 logger.debug("Skipping element " + e.getIdentifier() + " while merging " + dest + " with " + src);
101 continue;
102 } else if (MergeMode.Merge == mode) {
103 logger.debug("Renaming element " + e.getIdentifier() + " while merging " + dest + " with " + src);
104 e.setIdentifier(null);
105 dest.add(e);
106 } else if (MergeMode.Fail == mode) {
107 throw new MediaPackageException("Target media package " + dest + " already contains element with id "
108 + e.getIdentifier());
109 }
110 }
111 }
112 } catch (UnsupportedElementException e) {
113 throw new MediaPackageException(e);
114 }
115 return dest;
116 }
117
118
119
120
121
122
123
124
125 public static boolean contains(String identifier, MediaPackage mp) {
126 for (MediaPackageElement element : mp.getElements()) {
127 if (element.getIdentifier().equals(identifier))
128 return true;
129 }
130 return false;
131 }
132
133
134
135
136
137
138 public static Opt<String> getFileName(MediaPackageElement mpe) {
139 final URI uri = mpe.getURI();
140 if (uri != null) {
141 return Opt.nul(FilenameUtils.getName(uri.toString())).bind(Strings.blankToNone);
142 } else {
143 return Opt.none();
144 }
145 }
146
147
148
149
150
151
152
153 public static MediaPackage copy(MediaPackage mp) {
154 return (MediaPackage) mp.clone();
155 }
156
157
158 public static void updateElement(MediaPackage mp, MediaPackageElement e) {
159 mp.removeElementById(e.getIdentifier());
160 mp.add(e);
161 }
162
163
164
165
166 public static Consumer<MediaPackageElement> updateElement(final MediaPackage mp) {
167 return e -> updateElement(mp, e);
168 }
169
170 public static final Function<MediaPackageElement, String> getMediaPackageElementId = new Function<MediaPackageElement, String>() {
171 @Override
172 public String apply(MediaPackageElement mediaPackageElement) {
173 return mediaPackageElement.getIdentifier();
174 }
175 };
176
177
178 public static final class Filters {
179 private Filters() {
180 }
181
182
183
184 public static <A extends MediaPackageElement> Function<MediaPackageElement, List<A>> byType(final Class<A> type) {
185 return new Function<MediaPackageElement, List<A>>() {
186 @Override
187 public List<A> apply(MediaPackageElement mpe) {
188 return type.isAssignableFrom(mpe.getClass()) ? list((A) mpe) : (List<A>) NIL;
189 }
190 };
191 }
192
193 public static Function<MediaPackageElement, List<MediaPackageElement>> byFlavor(
194 final MediaPackageElementFlavor flavor) {
195 return new Function<MediaPackageElement, List<MediaPackageElement>>() {
196 @Override
197 public List<MediaPackageElement> apply(MediaPackageElement mpe) {
198
199 return flavor.matches(mpe.getFlavor()) ? Collections.singletonList(mpe) : Collections.emptyList();
200 }
201 };
202 }
203
204 public static <A extends MediaPackageElement> Function<MediaPackageElement, Boolean> ofType(final Class<A> type) {
205 return new Function<MediaPackageElement, Boolean>() {
206 @Override
207 public Boolean apply(MediaPackageElement mpe) {
208 return type.isAssignableFrom(mpe.getClass());
209 }
210 };
211 }
212
213 public static final Function<MediaPackageElement, List<Publication>> presentations = byType(Publication.class);
214
215 public static final Function<MediaPackageElement, List<Attachment>> attachments = byType(Attachment.class);
216
217 public static final Function<MediaPackageElement, List<Track>> tracks = byType(Track.class);
218
219 public static final Function<MediaPackageElement, List<Catalog>> catalogs = byType(Catalog.class);
220
221 public static final Function<MediaPackageElement, Boolean> isPublication = ofType(Publication.class);
222
223 public static final Function<MediaPackageElement, Boolean> isNotPublication = not(isPublication);
224
225 public static final Function<MediaPackageElement, Boolean> hasChecksum = new Function<MediaPackageElement, Boolean>() {
226 @Override
227 public Boolean apply(MediaPackageElement e) {
228 return e.getChecksum() != null;
229 }
230 };
231
232 public static final Function<MediaPackageElement, Boolean> hasNoChecksum = not(hasChecksum);
233
234 public static final Function<Track, Boolean> hasVideo = new Function<Track, Boolean>() {
235 @Override
236 public Boolean apply(Track track) {
237 return track.hasVideo();
238 }
239 };
240
241 public static final Function<Track, Boolean> hasAudio = new Function<Track, Boolean>() {
242 @Override
243 public Boolean apply(Track track) {
244 return track.hasAudio();
245 }
246 };
247
248 public static final Function<Track, Boolean> hasNoVideo = not(hasVideo);
249
250 public static final Function<Track, Boolean> hasNoAudio = not(hasAudio);
251
252
253 public static Function<Publication, Boolean> ofChannel(final String channelId) {
254 return new Function<Publication, Boolean>() {
255 @Override
256 public Boolean apply(Publication p) {
257 return p.getChannel().equals(channelId);
258 }
259 };
260 }
261
262
263 public static Function<MediaPackageElement, Boolean> hasTagAny(final List<String> tags) {
264 return new Function<MediaPackageElement, Boolean>() {
265 @Override
266 public Boolean apply(MediaPackageElement mpe) {
267 return mpe.containsTag(tags);
268 }
269 };
270 }
271
272
273
274
275
276
277 public static Function<MediaPackageElement, Boolean> matchesFlavor(final MediaPackageElementFlavor flavor) {
278 return new Function<MediaPackageElement, Boolean>() {
279 @Override
280 public Boolean apply(MediaPackageElement mpe) {
281
282 return flavor.matches(mpe.getFlavor());
283 }
284 };
285 }
286
287 public static final Function<MediaPackageElementFlavor, Function<MediaPackageElement, Boolean>> matchesFlavor = new Function<MediaPackageElementFlavor, Function<MediaPackageElement, Boolean>>() {
288 @Override
289 public Function<MediaPackageElement, Boolean> apply(final MediaPackageElementFlavor flavor) {
290 return matchesFlavor(flavor);
291 }
292 };
293
294 public static final Function<MediaPackageElement, Boolean> isEpisodeDublinCore = new Function<MediaPackageElement, Boolean>() {
295 @Override
296 public Boolean apply(MediaPackageElement mpe) {
297
298 return MediaPackageElements.EPISODE.matches(mpe.getFlavor());
299 }
300 };
301
302 public static final Function<MediaPackageElement, Boolean> isSmilCatalog = new Function<MediaPackageElement, Boolean>() {
303 @Override
304 public Boolean apply(MediaPackageElement mpe) {
305
306 return MediaPackageElements.SMIL.matches(mpe.getFlavor());
307 }
308 };
309 }
310
311
312
313
314
315
316
317
318
319
320
321 public static Option<List<String>> sanityCheck(MediaPackage mp) {
322 final Option<List<String>> errors = sequenceOpt(list(toOption(mp.getIdentifier() != null, "no ID"),
323 toOption(mp.getIdentifier() != null && isNotBlank(mp.getIdentifier().toString()), "blank ID")));
324 return errors.getOrElse(NIL).size() == 0 ? Option.<List<String>> none() : errors;
325 }
326
327
328 public static MediaPackage loadFromClassPath(String path) {
329 return withResource(MediaPackageSupport.class.getResourceAsStream(path),
330 new Function.X<InputStream, MediaPackage>() {
331 @Override
332 public MediaPackage xapply(InputStream is) throws MediaPackageException {
333 return MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().loadFromXml(is);
334 }
335 });
336 }
337
338
339
340
341 @Deprecated
342 public static final Function<MediaPackage, String> getId = new Function<MediaPackage, String>() {
343 @Override
344 public String apply(MediaPackage mp) {
345 return mp.getIdentifier().toString();
346 }
347 };
348
349
350 public static final class Fn {
351 private Fn() {
352 }
353
354 }
355 }