1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.opencastproject.mediapackage.elementbuilder;
24
25 import org.opencastproject.mediapackage.MediaPackageElement;
26 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
27 import org.opencastproject.mediapackage.MediaPackageReferenceImpl;
28 import org.opencastproject.mediapackage.MediaPackageSerializer;
29 import org.opencastproject.mediapackage.Track;
30 import org.opencastproject.mediapackage.UnsupportedElementException;
31 import org.opencastproject.mediapackage.track.AudioStreamImpl;
32 import org.opencastproject.mediapackage.track.SubtitleStreamImpl;
33 import org.opencastproject.mediapackage.track.TrackImpl;
34 import org.opencastproject.mediapackage.track.VideoStreamImpl;
35 import org.opencastproject.util.Checksum;
36 import org.opencastproject.util.MimeType;
37 import org.opencastproject.util.MimeTypes;
38
39 import org.apache.commons.lang3.StringUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
44
45 import java.net.URI;
46 import java.net.URISyntaxException;
47 import java.security.NoSuchAlgorithmException;
48
49 import javax.xml.xpath.XPathConstants;
50 import javax.xml.xpath.XPathException;
51 import javax.xml.xpath.XPathExpressionException;
52
53
54
55
56
57 public class TrackBuilderPlugin extends AbstractElementBuilderPlugin {
58
59
60
61
62 private static final Logger logger = LoggerFactory.getLogger(TrackBuilderPlugin.class);
63
64
65
66
67
68
69 @Override
70 public boolean accept(MediaPackageElement.Type type, MediaPackageElementFlavor flavor) {
71 return type.equals(MediaPackageElement.Type.Track);
72 }
73
74
75
76
77 @Override
78 public boolean accept(Node elementNode) {
79 String name = elementNode.getNodeName();
80 if (name.contains(":")) {
81 name = name.substring(name.indexOf(":") + 1);
82 }
83 return name.equalsIgnoreCase(MediaPackageElement.Type.Track.toString());
84 }
85
86
87
88
89
90
91 @Override
92 public boolean accept(URI uri, MediaPackageElement.Type type, MediaPackageElementFlavor flavor) {
93 return MediaPackageElement.Type.Track.equals(type);
94 }
95
96
97
98
99 @Override
100 public MediaPackageElement elementFromURI(URI uri) throws UnsupportedElementException {
101 logger.trace("Creating track from " + uri);
102 Track track = TrackImpl.fromURI(uri);
103 return track;
104 }
105
106
107
108
109
110
111 @Override
112 public MediaPackageElement newElement(MediaPackageElement.Type type, MediaPackageElementFlavor flavor) {
113 Track track = new TrackImpl();
114 track.setFlavor(flavor);
115 return track;
116 }
117
118
119
120
121
122
123 @Override
124 public MediaPackageElement elementFromManifest(Node elementNode, MediaPackageSerializer serializer)
125 throws UnsupportedElementException {
126
127 String id = null;
128 MimeType mimeType = null;
129 MediaPackageElementFlavor flavor = null;
130 TrackImpl.StreamingProtocol transport = null;
131 String reference = null;
132 URI url = null;
133 long size = -1;
134 Checksum checksum = null;
135
136 try {
137
138 id = (String) xpath.evaluate("@id", elementNode, XPathConstants.STRING);
139
140
141 url = serializer.decodeURI(new URI(xpath.evaluate("url/text()", elementNode).trim()));
142
143
144 reference = (String) xpath.evaluate("@ref", elementNode, XPathConstants.STRING);
145
146
147 String trackSize = xpath.evaluate("size/text()", elementNode).trim();
148 if (!"".equals(trackSize)) {
149 size = Long.parseLong(trackSize);
150 }
151
152
153 String flavorValue = (String) xpath.evaluate("@type", elementNode, XPathConstants.STRING);
154 if (StringUtils.isNotEmpty(flavorValue)) {
155 flavor = MediaPackageElementFlavor.parseFlavor(flavorValue);
156 }
157
158
159 String transportValue = (String) xpath.evaluate("@transport", elementNode, XPathConstants.STRING);
160 if (StringUtils.isNotEmpty(transportValue)) {
161 transport = TrackImpl.StreamingProtocol.valueOf(transportValue);
162 }
163
164
165 String checksumValue = (String) xpath.evaluate("checksum/text()", elementNode, XPathConstants.STRING);
166 String checksumType = (String) xpath.evaluate("checksum/@type", elementNode, XPathConstants.STRING);
167 if (StringUtils.isNotEmpty(checksumValue) && checksumType != null) {
168 checksum = Checksum.create(checksumType.trim(), checksumValue.trim());
169 }
170
171
172 String mimeTypeValue = (String) xpath.evaluate("mimetype/text()", elementNode, XPathConstants.STRING);
173 if (StringUtils.isNotEmpty(mimeTypeValue)) {
174 mimeType = MimeTypes.parseMimeType(mimeTypeValue);
175 }
176
177
178
179
180 TrackImpl track = TrackImpl.fromURI(url);
181
182 if (StringUtils.isNotBlank(id)) {
183 track.setIdentifier(id);
184 }
185
186
187 track.setURI(url);
188
189
190 if (StringUtils.isNotEmpty(reference)) {
191 track.referTo(MediaPackageReferenceImpl.fromString(reference));
192 }
193
194
195 if (size > 0) {
196 track.setSize(size);
197 }
198
199
200 if (checksum != null) {
201 track.setChecksum(checksum);
202 }
203
204
205 if (mimeType != null) {
206 track.setMimeType(mimeType);
207 }
208
209 if (flavor != null) {
210 track.setFlavor(flavor);
211 }
212
213
214 if (transport != null) {
215 track.setTransport(transport);
216 }
217
218
219 String description = (String) xpath.evaluate("description/text()", elementNode, XPathConstants.STRING);
220 if (StringUtils.isNotBlank(description)) {
221 track.setElementDescription(description.trim());
222 }
223
224
225 NodeList tagNodes = (NodeList) xpath.evaluate("tags/tag", elementNode, XPathConstants.NODESET);
226 for (int i = 0; i < tagNodes.getLength(); i++) {
227 track.addTag(tagNodes.item(i).getTextContent());
228 }
229
230
231 try {
232 String strDuration = (String) xpath.evaluate("duration/text()", elementNode, XPathConstants.STRING);
233 if (StringUtils.isNotEmpty(strDuration)) {
234 long duration = Long.parseLong(strDuration.trim());
235 track.setDuration(duration);
236 }
237 } catch (NumberFormatException e) {
238 throw new UnsupportedElementException("Duration of track " + url + " is malformatted");
239 }
240
241
242 String strLive = (String) xpath.evaluate("live/text()", elementNode, XPathConstants.STRING);
243 if (StringUtils.isNotEmpty(strLive)) {
244 boolean live = Boolean.parseBoolean(strLive.trim());
245 track.setLive(live);
246 }
247
248
249 String strMaster = (String) xpath.evaluate("master/text()", elementNode, XPathConstants.STRING);
250 if (StringUtils.isNotEmpty(strMaster)) {
251 track.setMaster(Boolean.parseBoolean(strMaster.trim()));
252 }
253
254
255 String strLogicalname = (String) xpath.evaluate("logicalname/text()", elementNode, XPathConstants.STRING);
256 if (StringUtils.isNotEmpty(strLogicalname)) {
257 track.setLogicalName(strLogicalname.trim());
258 }
259
260
261 Node audioSettingsNode = (Node) xpath.evaluate("audio", elementNode, XPathConstants.NODE);
262 if (audioSettingsNode != null && audioSettingsNode.hasChildNodes()) {
263 try {
264 AudioStreamImpl as = AudioStreamImpl.fromManifest(createStreamID(track), audioSettingsNode, xpath);
265 track.addStream(as);
266 } catch (IllegalStateException e) {
267 throw new UnsupportedElementException("Illegal state encountered while reading audio settings from " + url
268 + ": " + e.getMessage());
269 } catch (XPathException e) {
270 throw new UnsupportedElementException("Error while parsing audio settings from " + url + ": "
271 + e.getMessage());
272 }
273 }
274
275
276 Node videoSettingsNode = (Node) xpath.evaluate("video", elementNode, XPathConstants.NODE);
277 if (videoSettingsNode != null && videoSettingsNode.hasChildNodes()) {
278 try {
279 VideoStreamImpl vs = VideoStreamImpl.fromManifest(createStreamID(track), videoSettingsNode, xpath);
280 track.addStream(vs);
281 } catch (IllegalStateException e) {
282 throw new UnsupportedElementException("Illegal state encountered while reading video settings from " + url
283 + ": " + e.getMessage());
284 } catch (XPathException e) {
285 throw new UnsupportedElementException("Error while parsing video settings from " + url + ": "
286 + e.getMessage());
287 }
288 }
289
290
291 Node subtitleSettingsNode = (Node) xpath.evaluate("subtitle", elementNode, XPathConstants.NODE);
292 if (subtitleSettingsNode != null && subtitleSettingsNode.hasChildNodes()) {
293 try {
294 SubtitleStreamImpl ss = SubtitleStreamImpl.fromManifest(createStreamID(track), subtitleSettingsNode, xpath);
295 track.addStream(ss);
296 } catch (IllegalStateException e) {
297 throw new UnsupportedElementException("Illegal state encountered while reading subtitle settings from " + url
298 + ": " + e.getMessage());
299 } catch (XPathException e) {
300 throw new UnsupportedElementException("Error while parsing subtitle settings from " + url + ": "
301 + e.getMessage());
302 }
303 }
304
305 return track;
306 } catch (XPathExpressionException e) {
307 throw new UnsupportedElementException("Error while reading track information from manifest: " + e.getMessage());
308 } catch (NoSuchAlgorithmException e) {
309 throw new UnsupportedElementException("Unsupported digest algorithm: " + e.getMessage());
310 } catch (URISyntaxException e) {
311 throw new UnsupportedElementException("Error while reading presenter track " + url + ": " + e.getMessage());
312 }
313 }
314
315 private String createStreamID(Track track) {
316 return "stream-" + (track.getStreams().length + 1);
317 }
318
319 @Override
320 public String toString() {
321 return "Track Builder Plugin";
322 }
323
324 }