use of org.opencastproject.mediapackage.EName in project opencast by opencast.
the class IngestRestService method updateMediaPackageID.
/**
* Try updating the identifier of a mediapackage with the identifier from a episode DublinCore catalog.
*
* @param mp
* MediaPackage to modify
* @param is
* InputStream containing the episode DublinCore catalog
*/
private void updateMediaPackageID(MediaPackage mp, InputStream is) throws IOException {
DublinCoreCatalog dc = DublinCores.read(is);
EName en = new EName(DublinCore.TERMS_NS_URI, "identifier");
String id = dc.getFirst(en);
if (id != null) {
mp.setIdentifier(new IdImpl(id));
}
}
use of org.opencastproject.mediapackage.EName in project opencast by opencast.
the class IngestRestService method addMediaPackage.
@POST
@Produces(MediaType.TEXT_XML)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("addMediaPackage/{wdID}")
@RestQuery(name = "addMediaPackage", description = "<p>Create and ingest media package from media tracks with additional Dublin Core metadata. It is " + "mandatory to set a title for the recording. This can be done with the 'title' form field or by supplying a DC " + "catalog with a title included. The identifier of the newly created media package will be taken from the " + "<em>identifier</em> field or the episode DublinCore catalog (deprecated<sup>*</sup>). If no identifier is " + "set, a newa randumm UUIDv4 will be generated. This endpoint is not meant to be used by capture agents for " + "scheduled recordings. It's primary use is for manual ingests with command line tools like curl.</p> " + "<p>Multiple tracks can be ingested by using multiple form fields. It's important, however, to always set the " + "flavor of the next media file <em>before</em> sending the media file itself.</p>" + "<b>(*)</b> The special treatment of the identifier field is deprecated any may be removed in future versions " + "without further notice in favor of a random UUID generation to ensure uniqueness of identifiers. " + "<h3>Example curl command:</h3>" + "<p>Ingest one video file:</p>" + "<p><pre>\n" + "curl -f -i --digest -u opencast_system_account:CHANGE_ME -H 'X-Requested-Auth: Digest' \\\n" + " http://localhost:8080/ingest/addMediaPackage/fast -F creator='John Doe' -F title='Test Recording' \\\n" + " -F 'flavor=presentation/source' -F 'BODY=@test-recording.mp4' \n" + "</pre></p>" + "<p>Ingest two video files:</p>" + "<p><pre>\n" + "curl -f -i --digest -u opencast_system_account:CHANGE_ME -H 'X-Requested-Auth: Digest' \\\n" + " http://localhost:8080/ingest/addMediaPackage/fast -F creator='John Doe' -F title='Test Recording' \\\n" + " -F 'flavor=presentation/source' -F 'BODY=@test-recording-vga.mp4' \\\n" + " -F 'flavor=presenter/source' -F 'BODY=@test-recording-camera.mp4' \n" + "</pre></p>", pathParameters = { @RestParameter(description = "Workflow definition id", isRequired = true, name = "wdID", type = RestParameter.Type.STRING) }, restParameters = { @RestParameter(description = "The kind of media track. This has to be specified prior to each media track", isRequired = true, name = "flavor", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "abstract", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "accessRights", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "available", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "contributor", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "coverage", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "created", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "creator", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "date", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "description", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "extent", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "format", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "identifier", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "isPartOf", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "isReferencedBy", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "isReplacedBy", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "language", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "license", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "publisher", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "relation", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "replaces", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "rights", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "rightsHolder", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "source", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "spatial", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "subject", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "temporal", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "title", type = RestParameter.Type.STRING), @RestParameter(description = "Episode metadata value", isRequired = false, name = "type", type = RestParameter.Type.STRING), @RestParameter(description = "URL of episode DublinCore Catalog", isRequired = false, name = "episodeDCCatalogUri", type = RestParameter.Type.STRING), @RestParameter(description = "Episode DublinCore Catalog", isRequired = false, name = "episodeDCCatalog", type = RestParameter.Type.STRING), @RestParameter(description = "URL of series DublinCore Catalog", isRequired = false, name = "seriesDCCatalogUri", type = RestParameter.Type.STRING), @RestParameter(description = "Series DublinCore Catalog", isRequired = false, name = "seriesDCCatalog", type = RestParameter.Type.STRING), @RestParameter(description = "URL of a media track file", isRequired = false, name = "mediaUri", type = RestParameter.Type.STRING) }, bodyParameter = @RestParameter(description = "The media track file", isRequired = true, name = "BODY", type = RestParameter.Type.FILE), reponses = { @RestResponse(description = "Ingest successfull. Returns workflow instance as XML", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "Ingest failed due to invalid requests.", responseCode = HttpServletResponse.SC_BAD_REQUEST), @RestResponse(description = "Ingest failed. Something went wrong internally. Please have a look at the log files", responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR) }, returnDescription = "")
public Response addMediaPackage(@Context HttpServletRequest request, @PathParam("wdID") String wdID) {
logger.trace("add mediapackage as multipart-form-data with workflow definition id: {}", wdID);
MediaPackageElementFlavor flavor = null;
try {
MediaPackage mp = ingestService.createMediaPackage();
DublinCoreCatalog dcc = null;
Map<String, String> workflowProperties = new HashMap<>();
int seriesDCCatalogNumber = 0;
int episodeDCCatalogNumber = 0;
boolean hasMedia = false;
if (ServletFileUpload.isMultipartContent(request)) {
for (FileItemIterator iter = new ServletFileUpload().getItemIterator(request); iter.hasNext(); ) {
FileItemStream item = iter.next();
if (item.isFormField()) {
String fieldName = item.getFieldName();
String value = Streams.asString(item.openStream(), "UTF-8");
logger.trace("form field {}: {}", fieldName, value);
/* Ignore empty fields */
if ("".equals(value)) {
continue;
}
/* “Remember” the flavor for the next media. */
if ("flavor".equals(fieldName)) {
flavor = MediaPackageElementFlavor.parseFlavor(value);
/* Fields for DC catalog */
} else if (dcterms.contains(fieldName)) {
if ("identifier".equals(fieldName)) {
/* Use the identifier for the mediapackage */
mp.setIdentifier(new IdImpl(value));
}
EName en = new EName(DublinCore.TERMS_NS_URI, fieldName);
if (dcc == null) {
dcc = dublinCoreService.newInstance();
}
dcc.add(en, value);
/* Episode metadata by URL */
} else if ("episodeDCCatalogUri".equals(fieldName)) {
try {
URI dcurl = new URI(value);
updateMediaPackageID(mp, dcurl);
ingestService.addCatalog(dcurl, MediaPackageElements.EPISODE, mp);
episodeDCCatalogNumber += 1;
} catch (java.net.URISyntaxException e) {
/* Parameter was not a valid URL: Return 400 Bad Request */
logger.warn(e.getMessage(), e);
return Response.serverError().status(Status.BAD_REQUEST).build();
}
/* Episode metadata DC catalog (XML) as string */
} else if ("episodeDCCatalog".equals(fieldName)) {
InputStream is = new ByteArrayInputStream(value.getBytes("UTF-8"));
updateMediaPackageID(mp, is);
is.reset();
String fileName = "episode" + episodeDCCatalogNumber + ".xml";
episodeDCCatalogNumber += 1;
ingestService.addCatalog(is, fileName, MediaPackageElements.EPISODE, mp);
/* Series by URL */
} else if ("seriesDCCatalogUri".equals(fieldName)) {
try {
URI dcurl = new URI(value);
ingestService.addCatalog(dcurl, MediaPackageElements.SERIES, mp);
} catch (java.net.URISyntaxException e) {
/* Parameter was not a valid URL: Return 400 Bad Request */
logger.warn(e.getMessage(), e);
return Response.serverError().status(Status.BAD_REQUEST).build();
}
/* Series DC catalog (XML) as string */
} else if ("seriesDCCatalog".equals(fieldName)) {
String fileName = "series" + seriesDCCatalogNumber + ".xml";
seriesDCCatalogNumber += 1;
InputStream is = new ByteArrayInputStream(value.getBytes("UTF-8"));
ingestService.addCatalog(is, fileName, MediaPackageElements.SERIES, mp);
/* Add media files by URL */
} else if ("mediaUri".equals(fieldName)) {
if (flavor == null) {
/* A flavor has to be specified in the request prior the media file */
return Response.serverError().status(Status.BAD_REQUEST).build();
}
URI mediaUrl;
try {
mediaUrl = new URI(value);
} catch (java.net.URISyntaxException e) {
/* Parameter was not a valid URL: Return 400 Bad Request */
logger.warn(e.getMessage(), e);
return Response.serverError().status(Status.BAD_REQUEST).build();
}
ingestService.addTrack(mediaUrl, flavor, mp);
hasMedia = true;
} else {
/* Tread everything else as workflow properties */
workflowProperties.put(fieldName, value);
}
/* Media files as request parameter */
} else {
if (flavor == null) {
/* A flavor has to be specified in the request prior the video file */
logger.debug("A flavor has to be specified in the request prior to the content BODY");
return Response.serverError().status(Status.BAD_REQUEST).build();
}
ingestService.addTrack(item.openStream(), item.getName(), flavor, mp);
hasMedia = true;
}
}
/* Check if we got any media. Fail if not. */
if (!hasMedia) {
logger.warn("Rejected ingest without actual media.");
return Response.serverError().status(Status.BAD_REQUEST).build();
}
/* Add episode mediapackage if metadata were send separately */
if (dcc != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
dcc.toXml(out, true);
InputStream in = new ByteArrayInputStream(out.toByteArray());
ingestService.addCatalog(in, "dublincore.xml", MediaPackageElements.EPISODE, mp);
/* Check if we have metadata for the episode */
} else if (episodeDCCatalogNumber == 0) {
logger.warn("Rejected ingest without episode metadata. At least provide a title.");
return Response.serverError().status(Status.BAD_REQUEST).build();
}
WorkflowInstance workflow = (wdID == null) ? ingestService.ingest(mp) : ingestService.ingest(mp, wdID, workflowProperties);
return Response.ok(workflow).build();
}
return Response.serverError().status(Status.BAD_REQUEST).build();
} catch (Exception e) {
logger.warn(e.getMessage(), e);
return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
}
}
use of org.opencastproject.mediapackage.EName in project opencast by opencast.
the class IngestServiceImpl method schedule.
@Override
public void schedule(MediaPackage mediaPackage, String workflowDefinitionID, Map<String, String> properties) throws IllegalStateException, IngestException, NotFoundException, UnauthorizedException, SchedulerException {
MediaPackageElement[] mediaPackageElements = mediaPackage.getElementsByFlavor(MediaPackageElements.EPISODE);
if (mediaPackageElements.length != 1) {
logger.debug("There can be only one (and exactly one) episode dublin core catalog: https://youtu.be/_J3VeogFUOs");
throw new IngestException("There can be only one (and exactly one) episode dublin core catalog");
}
InputStream inputStream;
DublinCoreCatalog dublinCoreCatalog;
try {
inputStream = workingFileRepository.get(mediaPackage.getIdentifier().toString(), mediaPackageElements[0].getIdentifier());
dublinCoreCatalog = dublinCoreService.load(inputStream);
} catch (IOException e) {
throw new IngestException(e);
}
EName temporal = new EName(DublinCore.TERMS_NS_URI, "temporal");
List<DublinCoreValue> periods = dublinCoreCatalog.get(temporal);
if (periods.size() != 1) {
logger.debug("There can be only one (and exactly one) period");
throw new IngestException("There can be only one (and exactly one) period");
}
DCMIPeriod period = EncodingSchemeUtils.decodeMandatoryPeriod(periods.get(0));
if (!period.hasStart() || !period.hasEnd()) {
logger.debug("A scheduled recording needs to have a start and end.");
throw new IngestException("A scheduled recording needs to have a start and end.");
}
EName createdEName = new EName(DublinCore.TERMS_NS_URI, "created");
List<DublinCoreValue> created = dublinCoreCatalog.get(createdEName);
if (created.size() == 0) {
logger.debug("Created not set");
} else if (created.size() == 1) {
Date date = EncodingSchemeUtils.decodeMandatoryDate(created.get(0));
if (date.getTime() != period.getStart().getTime()) {
logger.debug("start and created date differ ({} vs {})", date.getTime(), period.getStart().getTime());
throw new IngestException("Temporal start and created date differ");
}
} else {
logger.debug("There can be only one created date");
throw new IngestException("There can be only one created date");
}
// spatial
EName spatial = new EName(DublinCore.TERMS_NS_URI, "spatial");
List<DublinCoreValue> captureAgents = dublinCoreCatalog.get(spatial);
if (captureAgents.size() != 1) {
logger.debug("Exactly one capture agent needs to be set");
throw new IngestException("Exactly one capture agent needs to be set");
}
String captureAgent = captureAgents.get(0).getValue();
// Go through properties
Map<String, String> agentProperties = new HashMap<>();
Map<String, String> workflowProperties = new HashMap<>();
for (String key : properties.keySet()) {
if (key.startsWith("org.opencastproject.workflow.config.")) {
workflowProperties.put(key, properties.get(key));
} else {
agentProperties.put(key, properties.get(key));
}
}
try {
schedulerService.addEvent(period.getStart(), period.getEnd(), captureAgent, new HashSet<>(), mediaPackage, workflowProperties, agentProperties, Opt.none(), Opt.none(), "ingest-service");
} finally {
for (MediaPackageElement mediaPackageElement : mediaPackage.getElements()) {
try {
workingFileRepository.delete(mediaPackage.getIdentifier().toString(), mediaPackageElement.getIdentifier());
} catch (IOException e) {
logger.warn("Failed to delete media package element", e);
}
}
}
}
use of org.opencastproject.mediapackage.EName in project opencast by opencast.
the class SeriesWorkflowOperationHandlerTest method testExtraMetadata.
@Test
public void testExtraMetadata() throws WorkflowOperationException {
final EName otherProperty = new EName(DublinCore.TERMS_NS_URI, "my-custom-property");
final String otherValue = "foobar";
// Add extra metadata to the series catalog.
seriesCatalog.set(DublinCore.PROPERTY_LANGUAGE, "Opencastian");
seriesCatalog.set(otherProperty, otherValue);
seriesCatalog.set(DublinCore.PROPERTY_CONTRIBUTOR, Arrays.asList(new DublinCoreValue[] { DublinCoreValue.mk("Mr. Contry Bute"), DublinCoreValue.mk("Mrs. Jane Doe") }));
// Prepare "copy metadata" property
String[] extraMetadata = { // Append a full metadata field, with NS
DublinCore.PROPERTY_LANGUAGE.toString(), // Field without namespace
DublinCore.PROPERTY_CONTRIBUTOR.getLocalName(), // Field with a namespace different than the default
otherProperty.toString(), // Field that does not exist in the series catalog
"does-not-exist" };
WorkflowInstanceImpl instance = new WorkflowInstanceImpl();
List<WorkflowOperationInstance> ops = new ArrayList<WorkflowOperationInstance>();
WorkflowOperationInstanceImpl operation = new WorkflowOperationInstanceImpl("test", OperationState.INSTANTIATED);
ops.add(operation);
instance.setOperations(ops);
instance.setMediaPackage(mp);
MediaPackage clone = (MediaPackage) mp.clone();
operation.setConfiguration(SeriesWorkflowOperationHandler.SERIES_PROPERTY, "series1");
operation.setConfiguration(SeriesWorkflowOperationHandler.ATTACH_PROPERTY, "*");
operation.setConfiguration(SeriesWorkflowOperationHandler.APPLY_ACL_PROPERTY, "false");
operation.setConfiguration(SeriesWorkflowOperationHandler.COPY_METADATA_PROPERTY, StringUtils.join(extraMetadata, ", "));
WorkflowOperationResult result = operationHandler.start(instance, null);
Assert.assertEquals(Action.CONTINUE, result.getAction());
MediaPackage resultingMediapackage = result.getMediaPackage();
// Get episode DublinCore
DublinCoreCatalog episodeCatalog = DublinCores.read(capturedStream.getValue());
Assert.assertEquals("series1", resultingMediapackage.getSeries());
Assert.assertEquals("Series 1", resultingMediapackage.getSeriesTitle());
Assert.assertEquals(clone.getElements().length + 1, resultingMediapackage.getElements().length);
// Check the extra metadata were copied into the dublincore (only those present in the series catalog)
Assert.assertTrue(episodeCatalog.hasValue(DublinCore.PROPERTY_CONTRIBUTOR));
Assert.assertEquals(seriesCatalog.get(DublinCore.PROPERTY_CONTRIBUTOR), episodeCatalog.get(DublinCore.PROPERTY_CONTRIBUTOR));
Assert.assertTrue(episodeCatalog.hasValue(DublinCore.PROPERTY_LANGUAGE));
Assert.assertEquals(seriesCatalog.get(DublinCore.PROPERTY_LANGUAGE), episodeCatalog.get(DublinCore.PROPERTY_LANGUAGE));
Assert.assertTrue(episodeCatalog.hasValue(otherProperty));
Assert.assertEquals(seriesCatalog.get(otherProperty), episodeCatalog.get(otherProperty));
Assert.assertFalse(episodeCatalog.hasValue(new EName(DublinCore.TERMS_NS_URI, "does-not-exist")));
}
use of org.opencastproject.mediapackage.EName in project opencast by opencast.
the class IndexServiceImpl method createSeries.
@Override
public String createSeries(String metadata) throws IllegalArgumentException, IndexServiceException, UnauthorizedException {
JSONObject metadataJson = null;
try {
metadataJson = (JSONObject) new JSONParser().parse(metadata);
} catch (Exception e) {
logger.warn("Unable to parse metadata {}", metadata);
throw new IllegalArgumentException("Unable to parse metadata" + metadata);
}
if (metadataJson == null)
throw new IllegalArgumentException("No metadata set to create series");
JSONArray seriesMetadataJson = (JSONArray) metadataJson.get("metadata");
if (seriesMetadataJson == null)
throw new IllegalArgumentException("No metadata field in metadata");
JSONObject options = (JSONObject) metadataJson.get("options");
if (options == null)
throw new IllegalArgumentException("No options field in metadata");
Opt<Long> themeId = Opt.none();
Long theme = (Long) metadataJson.get("theme");
if (theme != null) {
themeId = Opt.some(theme);
}
Map<String, String> optionsMap;
try {
optionsMap = JSONUtils.toMap(new org.codehaus.jettison.json.JSONObject(options.toJSONString()));
} catch (JSONException e) {
logger.warn("Unable to parse options to map: {}", getStackTrace(e));
throw new IllegalArgumentException("Unable to parse options to map");
}
DublinCoreCatalog dc = DublinCores.mkOpencastSeries().getCatalog();
dc.set(PROPERTY_IDENTIFIER, UUID.randomUUID().toString());
dc.set(DublinCore.PROPERTY_CREATED, EncodingSchemeUtils.encodeDate(new Date(), Precision.Second));
for (Entry<String, String> entry : optionsMap.entrySet()) {
dc.set(new EName(DublinCores.OC_PROPERTY_NS_URI, entry.getKey()), entry.getValue());
}
MetadataList metadataList;
try {
metadataList = getMetadataListWithAllSeriesCatalogUIAdapters();
metadataList.fromJSON(seriesMetadataJson.toJSONString());
} catch (Exception e) {
logger.warn("Not able to parse the series metadata {}: {}", seriesMetadataJson, getStackTrace(e));
throw new IllegalArgumentException("Not able to parse the series metadata");
}
Opt<MetadataCollection> seriesMetadata = metadataList.getMetadataByFlavor(MediaPackageElements.SERIES.toString());
if (seriesMetadata.isSome()) {
DublinCoreMetadataUtil.updateDublincoreCatalog(dc, seriesMetadata.get());
}
AccessControlList acl = getAccessControlList(metadataJson);
String seriesId;
try {
DublinCoreCatalog createdSeries = seriesService.updateSeries(dc);
seriesId = createdSeries.getFirst(PROPERTY_IDENTIFIER);
seriesService.updateAccessControl(seriesId, acl);
for (Long id : themeId) seriesService.updateSeriesProperty(seriesId, THEME_PROPERTY_NAME, Long.toString(id));
} catch (Exception e) {
logger.error("Unable to create new series: {}", getStackTrace(e));
throw new IndexServiceException("Unable to create new series");
}
updateSeriesMetadata(seriesId, metadataList);
return seriesId;
}
Aggregations