Skip to content

Commit

Permalink
BXC-4630 derivative MP3 from WAV (#1789)
Browse files Browse the repository at this point in the history
* generate mp3 derivatives from wav files

* generate mp3 derivatives from wav files

* fix destroy derivative and enhancement tests

* fix javadoc, unique startuporder value and route id, add log statements/test/assertions, use FcrepoJmsConstants, update context file

* remove constructor, rename bean to addAudioAccessCopyProcessor, add test, update context files

* rename bean addAudioAccessCopyProcessor in context file

* add audio/wave and audio/x-wave to mimetypes

* switch mp3 to m4a

* add audioDerivativeProcessor to service-context

* change audio temp path from -access to -audio
  • Loading branch information
krwong committed Sep 10, 2024
1 parent 87f58e1 commit c985fc7
Show file tree
Hide file tree
Showing 17 changed files with 476 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class DatastreamPermissionUtil {
DS_PERMISSION_MAP = new EnumMap<>(DatastreamType.class);
DS_PERMISSION_MAP.put(DatastreamType.FULLTEXT_EXTRACTION, Permission.viewHidden);
DS_PERMISSION_MAP.put(DatastreamType.JP2_ACCESS_COPY, Permission.viewAccessCopies);
DS_PERMISSION_MAP.put(DatastreamType.AUDIO_ACCESS_COPY, Permission.viewAccessCopies);
DS_PERMISSION_MAP.put(DatastreamType.ACCESS_SURROGATE, Permission.viewAccessCopies);
DS_PERMISSION_MAP.put(DatastreamType.MD_DESCRIPTIVE, Permission.viewMetadata);
DS_PERMISSION_MAP.put(DatastreamType.MD_DESCRIPTIVE_HISTORY, Permission.viewHidden);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
public enum DatastreamType {
ACCESS_SURROGATE("access_surrogate", "application/octet-stream", null, null, EXTERNAL),
AUDIO_ACCESS_COPY("audio", "audio/aac", "m4a", null, EXTERNAL),
FULLTEXT_EXTRACTION("fulltext", "text/plain", "txt", null, EXTERNAL),
JP2_ACCESS_COPY("jp2", "image/jp2", "jp2", null, EXTERNAL),
MD_DESCRIPTIVE("md_descriptive", "text/xml", "xml", METADATA_CONTAINER, INTERNAL),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package edu.unc.lib.boxc.services.camel.audio;

import edu.unc.lib.boxc.services.camel.util.CdrFcrepoHeaders;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.regex.Pattern;

/**
* Processor which validates and prepares audio objects for producing derivatives
* @author krwong
*/
public class AudioDerivativeProcessor implements Processor {
private static final Logger log = LoggerFactory.getLogger(AudioDerivativeProcessor.class);

private static final Pattern MIMETYPE_PATTERN = Pattern.compile("^(audio.(wav|wave|x-wave))$");

/**
* Returns true if the subject of the exchange is a binary which
* is eligible for having audio derivatives generated from it.
* @param exchange
* @return
*/
public static boolean allowedAudioType(Exchange exchange) {
Message in = exchange.getIn();
String mimetype = (String) in.getHeader(CdrFcrepoHeaders.CdrBinaryMimeType);
String binPath = (String) in.getHeader(CdrFcrepoHeaders.CdrBinaryPath);

if (!MIMETYPE_PATTERN.matcher(mimetype).matches()) {
log.debug("File type {} on object {} is not applicable for audio derivatives", mimetype, binPath);
return false;
}

log.debug("Object {} with type {} is permitted for audio derivatives", binPath, mimetype);
return true;
}

@Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
String mimetype = (String) in.getHeader(CdrFcrepoHeaders.CdrBinaryMimeType);

String binPath = (String) in.getHeader(CdrFcrepoHeaders.CdrBinaryPath);
log.debug("Keeping existing audio path as {} for type {}", binPath, mimetype);
in.setHeader(CdrFcrepoHeaders.CdrAudioPath, binPath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package edu.unc.lib.boxc.services.camel.audio;

import edu.unc.lib.boxc.model.api.exceptions.RepositoryException;
import edu.unc.lib.boxc.services.camel.images.AddDerivativeProcessor;
import edu.unc.lib.boxc.services.camel.util.CdrFcrepoHeaders;
import org.apache.camel.BeanInject;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spi.UuidGenerator;
import org.apache.camel.support.DefaultUuidGenerator;
import org.slf4j.Logger;

import static org.slf4j.LoggerFactory.getLogger;

/**
* Router which triggers the creation of audio derivatives
* @author krwong
*/
public class AudioEnhancementsRouter extends RouteBuilder {
private static final Logger log = getLogger(AudioEnhancementsRouter.class);

@BeanInject(value = "addAudioAccessCopyProcessor")
private AddDerivativeProcessor addAudioAccessCopyProcessor;

private UuidGenerator uuidGenerator;

/**
* Configure the audio enhancement route workflow.
*/
@Override
public void configure() throws Exception {
AudioDerivativeProcessor audioDerivProcessor = new AudioDerivativeProcessor();

uuidGenerator = new DefaultUuidGenerator();

onException(RepositoryException.class)
.redeliveryDelay("{{error.retryDelay}}")
.maximumRedeliveries("{{error.maxRedeliveries}}")
.backOffMultiplier("{{error.backOffMultiplier}}")
.retryAttemptedLogLevel(LoggingLevel.WARN);

from("direct:process.enhancement.audioAccessCopy")
.routeId("AudioAccessCopy")
.startupOrder(25)
.log(LoggingLevel.DEBUG, log, "Access copy triggered")
.filter().method(addAudioAccessCopyProcessor, "needsRun")
.filter().method(audioDerivProcessor, "allowedAudioType")
.bean(audioDerivProcessor)
.log(LoggingLevel.INFO, log, "Creating/Updating AAC access copy for ${headers[CdrAudioPath]}")
// Generate an random identifier to avoid derivative collisions
.setBody(exchange -> uuidGenerator.generateUuid())
.setHeader(CdrFcrepoHeaders.CdrTempPath, simple("${properties:services.tempDirectory}/${body}-audio"))
.doTry()
.recipientList(simple("exec:/bin/sh?args=${properties:cdr.enhancement.bin}/convertWav.sh "
+ "${headers[CdrAudioPath]} ${headers[CdrTempPath]}"))
.bean(addAudioAccessCopyProcessor)
.endDoTry()
.doFinally()
.bean(addAudioAccessCopyProcessor, "cleanupTempFile")
.end();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static edu.unc.lib.boxc.services.camel.util.CdrFcrepoHeaders.CdrObjectType;
import static org.slf4j.LoggerFactory.getLogger;

import edu.unc.lib.boxc.services.camel.audio.AudioDerivativeProcessor;
import org.apache.camel.BeanInject;
import org.apache.camel.LoggingLevel;
import org.apache.camel.PropertyInject;
Expand Down Expand Up @@ -40,6 +41,9 @@ public class DestroyDerivativesRouter extends RouteBuilder {
@BeanInject(value = "destroyFulltextProcessor")
private DestroyDerivativesProcessor destroyFulltextProcessor;

@BeanInject(value = "destroyAudioProcessor")
private DestroyDerivativesProcessor destroyAudioProcessor;

private String destroyDerivativesStreamCamel;
private long errorRetryDelay;
private int errorMaxRedeliveries;
Expand All @@ -63,6 +67,8 @@ public void configure() throws Exception {
.to("direct:image.derivatives.destroy")
.when(method(FulltextProcessor.class, "allowedTextType"))
.to("direct:fulltext.derivatives.destroy")
.when(method(AudioDerivativeProcessor.class, "allowedAudioType"))
.to("direct:audio.derivatives.destroy")
.end();

from("direct:fulltext.derivatives.destroy")
Expand Down Expand Up @@ -95,6 +101,12 @@ public void configure() throws Exception {
.startupOrder(200)
.log(LoggingLevel.DEBUG, log, "Destroying collection image upload")
.bean(destroyCollectionSrcImgProcessor);

from("direct:audio.derivatives.destroy")
.routeId("CdrDestroyAudio")
.startupOrder(199)
.log(LoggingLevel.DEBUG, log, "Destroying derivative audio files")
.bean(destroyAudioProcessor);
}

public void setDestroyedMsgProcessor(DestroyedMsgProcessor destroyedMsgProcessor) {
Expand All @@ -121,6 +133,10 @@ public void setDestroyFulltextProcessor(DestroyDerivativesProcessor destroyFullt
this.destroyFulltextProcessor = destroyFulltextProcessor;
}

public void setDestroyAudioProcessor(DestroyDerivativesProcessor destroyAudioProcessor) {
this.destroyAudioProcessor = destroyAudioProcessor;
}

@PropertyInject("cdr.destroy.derivatives.stream.camel")
public void setDestroyDerivativesStreamCamel(String destroyDerivativesStreamCamel) {
this.destroyDerivativesStreamCamel = destroyDerivativesStreamCamel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class EnhancementRouter extends RouteBuilder {
@PropertyInject(value = "cdr.enhancement.processingThreads")
private Integer enhancementThreads;

private static final String DEFAULT_ENHANCEMENTS = "thumbnails,imageAccessCopy,extractFulltext";
private static final String DEFAULT_ENHANCEMENTS = "thumbnails,imageAccessCopy,extractFulltext,audioAccessCopy";
private static final String THUMBNAIL_ENHANCEMENTS = "thumbnails";
@Override
public void configure() throws Exception {
Expand Down Expand Up @@ -84,7 +84,7 @@ public void configure() throws Exception {
.filter(header(CdrBinaryPath).isNotNull())
.to("{{cdr.enhancement.perform.camel}}");

// Queue for executing enhancnement operations
// Queue for executing enhancement operations
from("{{cdr.enhancement.perform.camel}}")
.routeId("PerformEnhancementsQueue")
.startupOrder(107)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public abstract class CdrFcrepoHeaders {
// URI identifying the location
public static final String CdrImagePath = "CdrImagePath";

// URI identifying the location
public static final String CdrAudioPath = "CdrAudioPath";

// File path for a temp file
public static final String CdrTempPath = "CdrTempPath";

Expand Down
14 changes: 14 additions & 0 deletions services-camel-app/src/main/webapp/WEB-INF/service-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@
<constructor-arg value="${cdr.enhancement.path.fulltext}" />
</bean>

<bean id="addAudioAccessCopyProcessor" class="edu.unc.lib.boxc.services.camel.images.AddDerivativeProcessor">
<constructor-arg value="#{T(edu.unc.lib.boxc.model.api.DatastreamType).AUDIO_ACCESS_COPY.getExtension()}" />
<constructor-arg value="${cdr.enhancement.path.audio}" />
</bean>

<bean id="audioAccessCopyProcessor" class="edu.unc.lib.boxc.services.camel.audio.AudioDerivativeProcessor">
</bean>

<bean id="destroyedMsgProcessor" class="edu.unc.lib.boxc.services.camel.destroyDerivatives.DestroyedMsgProcessor">
<constructor-arg value="${sourceImages.dir}" />
</bean>
Expand Down Expand Up @@ -263,6 +271,11 @@
<constructor-arg value="#{T(edu.unc.lib.boxc.model.api.DatastreamType).FULLTEXT_EXTRACTION.getExtension()}" />
<constructor-arg value="${cdr.enhancement.path.fulltext}" />
</bean>

<bean id="destroyAudioProcessor" class="edu.unc.lib.boxc.services.camel.destroyDerivatives.DestroyDerivativesProcessor">
<constructor-arg value="#{T(edu.unc.lib.boxc.model.api.DatastreamType).AUDIO_ACCESS_COPY.getExtension()}" />
<constructor-arg value="${cdr.enhancement.path.audio}" />
</bean>

<bean id="indexingMessageProcessor" class="edu.unc.lib.boxc.services.camel.triplesReindexing.IndexingMessageProcessor">
</bean>
Expand Down Expand Up @@ -566,6 +579,7 @@
<camel:package>edu.unc.lib.boxc.services.camel.enhancements</camel:package>
<camel:package>edu.unc.lib.boxc.services.camel.images</camel:package>
<camel:package>edu.unc.lib.boxc.services.camel.fulltext</camel:package>
<camel:package>edu.unc.lib.boxc.services.camel.audio</camel:package>
<camel:package>edu.unc.lib.boxc.services.camel.solr</camel:package>
<camel:package>edu.unc.lib.boxc.services.camel.binaryCleanup</camel:package>
<!-- Initialize metaServicesRouter after the routes it depends on -->
Expand Down
Loading

0 comments on commit c985fc7

Please sign in to comment.