Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/main/java/edu/ucla/library/avpairtree/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public final class Config {
*/
public static final String ACCESS_URL_PATTERN = "iiif.access.url";

/**
* The configuration property for which substitution pattern in iiif.access.url should be the ID; this is 1-based,
* not zero-based.
*/
public static final String ACCESS_URL_ID_INDEX = "iiif.access.url.id.index";

/**
* The number of workers that should work to do media file conversions.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.csveed.api.CsvClient;
import org.csveed.api.CsvClientImpl;
Expand Down Expand Up @@ -45,6 +47,8 @@ public class WatcherVerticle extends AbstractVerticle {

private static final Logger LOGGER = LoggerFactory.getLogger(WatcherVerticle.class, MessageCodes.BUNDLE);

private static final String SUBSTITUTION_PATTERN = "{}";

@Override
public void start(final Promise<Void> aPromise) {
final DeliveryOptions options = new DeliveryOptions().setSendTimeout(Integer.MAX_VALUE);
Expand Down Expand Up @@ -152,7 +156,7 @@ private Future<String> updateCSV(final String aCsvFilePath, final Map<String, Cs
}

if (aArkMap.containsKey(ark)) {
columns[columnCount] = encodeAccessURL(aArkMap.get(ark));
columns[columnCount] = constructAccessURL(aArkMap.get(ark));
}
} else {
columns = new String[columnCount];
Expand All @@ -161,7 +165,7 @@ private Future<String> updateCSV(final String aCsvFilePath, final Map<String, Cs
if (accessUrlIndex != index) {
columns[index] = row.get(index + 1); // Strangely, row is 1-based
} else if (aArkMap.containsKey(ark)) {
columns[index] = encodeAccessURL(aArkMap.get(ark));
columns[index] = constructAccessURL(aArkMap.get(ark));
}
}
}
Expand Down Expand Up @@ -194,25 +198,95 @@ private int getUrlAccessIndex(final Header aHeader) {
}

/**
* Encodes the Pairtree path for the A/V server's access URL.
* Encodes the Pairtree path in the A/V server's access URL.
*
* @param aCsvItem An item from the CSV file
* @return An encoded path for the A/V server's access URL
* @throws IndexOutOfBoundsException If a supplied ID index position isn't valid
* @throws UnsupportedOperationException If the access URL pattern doesn't contain any placeholders
*/
private String encodeAccessURL(final CsvItem aCsvItem) {
private String constructAccessURL(final CsvItem aCsvItem)
throws IndexOutOfBoundsException, UnsupportedOperationException {
final String arkPrefix = config().getString(Config.PAIRTREE_PREFIX);
final String ark = aCsvItem.getItemARK();
final String pathARK = ark.replace(arkPrefix, Constants.EMPTY); // Strip ARK prefix
final String slug = aCsvItem.getPathRoot();
final String encodedARK = PairtreeUtils.encodeID(ark);
final String fileExt = Constants.PERIOD + config().getString(Config.ENCODING_FORMAT);
final String accessUrlPattern = config().getString(Config.ACCESS_URL_PATTERN, "{}");
final String accessUrlPattern = config().getString(Config.ACCESS_URL_PATTERN, SUBSTITUTION_PATTERN);
final int urlPatternIdIndex = config().getInteger(Config.ACCESS_URL_ID_INDEX, 1);
final String ptPath = PairtreeUtils.mapToPtPath(slug + Constants.SLASH + Pairtree.ROOT, pathARK, pathARK);
final String ptFile = (ptPath + Constants.SLASH + encodedARK + fileExt).replace(Constants.PLUS, "%2B");
final String accessURL = StringUtils.format(accessUrlPattern, ptFile);
final String ptFilePath = (ptPath + Constants.SLASH + encodedARK + fileExt).replace(Constants.PLUS, "%2B");
final String accessURL = addIdPath(accessUrlPattern, urlPatternIdIndex, ptFilePath);

LOGGER.debug(MessageCodes.AVPT_010, ark, accessURL);

return accessURL;
}

/**
* Gets the IIIF access URL.
*
* @param aAccessUrlPattern A textual pattern to use for the IIIF access URL
* @param aUrlPatternIdIndex A position for the ID in the access URL pattern
* @param aIdPath An ID's Pairtree path
* @return The IIIF access URL
* @throws IndexOutOfBoundsException If a supplied ID index position isn't valid
* @throws UnsupportedOperationException If the access URL pattern doesn't contain any placeholders
*/
private String addIdPath(final String aAccessUrlPattern, final int aUrlPatternIdIndex, final String aIdPath)
throws IndexOutOfBoundsException, UnsupportedOperationException {
final int substitutionCount = countSubstitutionPatterns(aAccessUrlPattern);

// We allow up to three path substitutions, with the Pairtree path being able to be swapped into any of them
switch (substitutionCount) {
case 3:
if (aUrlPatternIdIndex == 1) {
return StringUtils.format(aAccessUrlPattern, aIdPath, SUBSTITUTION_PATTERN, SUBSTITUTION_PATTERN);
} else if (aUrlPatternIdIndex == 2) {
return StringUtils.format(aAccessUrlPattern, SUBSTITUTION_PATTERN, aIdPath, SUBSTITUTION_PATTERN);
} else if (aUrlPatternIdIndex == 3) {
return StringUtils.format(aAccessUrlPattern, SUBSTITUTION_PATTERN, SUBSTITUTION_PATTERN, aIdPath);
} else {
throw new IndexOutOfBoundsException(LOGGER.getMessage(MessageCodes.AVPT_013, aUrlPatternIdIndex));
}
case 2:
if (aUrlPatternIdIndex == 1) {
return StringUtils.format(aAccessUrlPattern, aIdPath, SUBSTITUTION_PATTERN);
} else if (aUrlPatternIdIndex == 2) {
return StringUtils.format(aAccessUrlPattern, SUBSTITUTION_PATTERN, aIdPath);
} else {
throw new IndexOutOfBoundsException(LOGGER.getMessage(MessageCodes.AVPT_013, aUrlPatternIdIndex));
}
case 1:
if (aUrlPatternIdIndex == 1) {
return StringUtils.format(aAccessUrlPattern, aIdPath);
} else {
throw new IndexOutOfBoundsException(LOGGER.getMessage(MessageCodes.AVPT_013, aUrlPatternIdIndex));
}
default:
throw new UnsupportedOperationException(LOGGER.getMessage(MessageCodes.AVPT_013, substitutionCount));
}
}

/**
* Counts the number of substitution patterns in the supplied string.
*
* @param aString A string with substitution patterns (e.g. <code>{}</code>)
* @return The number of substitution patterns in the supplied string
*/
private int countSubstitutionPatterns(final String aString) {
final Pattern pattern = Pattern.compile(SUBSTITUTION_PATTERN, Pattern.LITERAL);
final Matcher matcher = pattern.matcher(aString);

int startIndex = 0;
int count = 0;

while (matcher.find(startIndex)) {
startIndex = matcher.start() + 1;
count += 1;
}

return count;
}
}
2 changes: 2 additions & 0 deletions src/main/resources/av-pairtree_messages.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@
<entry key="AVPT_010">IIIF access URL created for '{}': {}</entry>
<entry key="AVPT_011">{} worker starting in: {}</entry>
<entry key="AVPT_012">{} worker pool size set to '{}' workers</entry>
<entry key="AVPT_013">Index for IIIF access URL doesn't correspond to number of substitution patterns: {}</entry>
<entry key="AVPT_014">IIIF access URL pattern doesn't contain a supported number of substitution patterns</entry>

</properties>
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private Future<Void> checkOutput(final String aFileName, final int aLatchCount,
final AtomicInteger counter = new AtomicInteger(0);

reader.lines().forEach(line -> {
if (line.contains(avServer)) {
if (line.contains(avServer) && line.contains(".mp4{}")) {
counter.incrementAndGet();
}
});
Expand Down
7 changes: 5 additions & 2 deletions src/test/resources/test-config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ audio.codec = aac
audio.bit.rate = 320000
audio.channels = 2

# The URL pattern for our streaming A/V server
iiif.access.url = https://wowza.library.ucla.edu/iiif_av_public/definst/mp4:{}/manifest.mpd
# The URL pattern for our streaming A/V server; it can have up to three substitution patterns (i.e., "{}")
iiif.access.url = https://wowza.library.ucla.edu/iiif_av_public/definst/mp4:{}{}

# The 1-based position of the substitution pattern to use for the Pairtree path
iiif.access.url.id.index = 1

# The number of threads working on media file conversions
conversion.workers = 2