mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-18 15:33:09 +00:00
DS-528 RSS feed to support iTunes Podcast
git-svn-id: http://scm.dspace.org/svn/repo/dspace/trunk@6531 9c30dcfa-912a-0410-8fc2-9e0234be79fd
This commit is contained in:
@@ -272,6 +272,11 @@
|
|||||||
<artifactId>contiperf</artifactId>
|
<artifactId>contiperf</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.rometools</groupId>
|
||||||
|
<artifactId>rome-modules</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@@ -9,13 +9,16 @@ package org.dspace.app.util;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.lang.ArrayUtils;
|
import org.apache.commons.lang.ArrayUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
import org.dspace.content.Bitstream;
|
import org.dspace.content.Bitstream;
|
||||||
@@ -25,6 +28,7 @@ import org.dspace.content.DCDate;
|
|||||||
import org.dspace.content.DCValue;
|
import org.dspace.content.DCValue;
|
||||||
import org.dspace.content.DSpaceObject;
|
import org.dspace.content.DSpaceObject;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.MetadataSchema;
|
||||||
import org.dspace.core.ConfigurationManager;
|
import org.dspace.core.ConfigurationManager;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.handle.HandleManager;
|
import org.dspace.handle.HandleManager;
|
||||||
@@ -33,6 +37,8 @@ import com.sun.syndication.feed.synd.SyndFeed;
|
|||||||
import com.sun.syndication.feed.synd.SyndFeedImpl;
|
import com.sun.syndication.feed.synd.SyndFeedImpl;
|
||||||
import com.sun.syndication.feed.synd.SyndEntry;
|
import com.sun.syndication.feed.synd.SyndEntry;
|
||||||
import com.sun.syndication.feed.synd.SyndEntryImpl;
|
import com.sun.syndication.feed.synd.SyndEntryImpl;
|
||||||
|
import com.sun.syndication.feed.synd.SyndEnclosure;
|
||||||
|
import com.sun.syndication.feed.synd.SyndEnclosureImpl;
|
||||||
import com.sun.syndication.feed.synd.SyndImage;
|
import com.sun.syndication.feed.synd.SyndImage;
|
||||||
import com.sun.syndication.feed.synd.SyndImageImpl;
|
import com.sun.syndication.feed.synd.SyndImageImpl;
|
||||||
import com.sun.syndication.feed.synd.SyndPerson;
|
import com.sun.syndication.feed.synd.SyndPerson;
|
||||||
@@ -42,10 +48,13 @@ import com.sun.syndication.feed.synd.SyndContentImpl;
|
|||||||
import com.sun.syndication.feed.module.DCModuleImpl;
|
import com.sun.syndication.feed.module.DCModuleImpl;
|
||||||
import com.sun.syndication.feed.module.DCModule;
|
import com.sun.syndication.feed.module.DCModule;
|
||||||
import com.sun.syndication.feed.module.Module;
|
import com.sun.syndication.feed.module.Module;
|
||||||
|
import com.sun.syndication.feed.module.itunes.*;
|
||||||
|
import com.sun.syndication.feed.module.itunes.types.Duration;
|
||||||
import com.sun.syndication.io.SyndFeedOutput;
|
import com.sun.syndication.io.SyndFeedOutput;
|
||||||
import com.sun.syndication.io.FeedException;
|
import com.sun.syndication.io.FeedException;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
import org.dspace.content.Bundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke ROME library to assemble a generic model of a syndication
|
* Invoke ROME library to assemble a generic model of a syndication
|
||||||
@@ -80,6 +89,7 @@ public class SyndicationFeed
|
|||||||
private static String defaultAuthorField = "dc.contributor.author";
|
private static String defaultAuthorField = "dc.contributor.author";
|
||||||
private static String defaultDateField = "dc.date.issued";
|
private static String defaultDateField = "dc.date.issued";
|
||||||
private static String defaultDescriptionFields = "dc.description.abstract, dc.description, dc.title.alternative, dc.title";
|
private static String defaultDescriptionFields = "dc.description.abstract, dc.description, dc.title.alternative, dc.title";
|
||||||
|
private static String defaultExternalMedia = "dc.source.uri";
|
||||||
|
|
||||||
// metadata field for Item title in entry:
|
// metadata field for Item title in entry:
|
||||||
private static String titleField =
|
private static String titleField =
|
||||||
@@ -96,6 +106,9 @@ public class SyndicationFeed
|
|||||||
private static String authorField =
|
private static String authorField =
|
||||||
getDefaultedConfiguration("webui.feed.item.author", defaultAuthorField);
|
getDefaultedConfiguration("webui.feed.item.author", defaultAuthorField);
|
||||||
|
|
||||||
|
// metadata field for Item external media source url
|
||||||
|
private static String sourceField = getDefaultedConfiguration("webui.feed.item.sourceuri", defaultExternalMedia);
|
||||||
|
|
||||||
// metadata field for Item dc:creator field in entry's DCModule (no default)
|
// metadata field for Item dc:creator field in entry's DCModule (no default)
|
||||||
private static String dcCreatorField = ConfigurationManager.getProperty("webui.feed.item.dc.creator");
|
private static String dcCreatorField = ConfigurationManager.getProperty("webui.feed.item.dc.creator");
|
||||||
|
|
||||||
@@ -115,6 +128,8 @@ public class SyndicationFeed
|
|||||||
// affects Bitstream retrieval URL and I18N keys
|
// affects Bitstream retrieval URL and I18N keys
|
||||||
private String uiType = null;
|
private String uiType = null;
|
||||||
|
|
||||||
|
private HttpServletRequest request = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param ui either "xmlui" or "jspui"
|
* @param ui either "xmlui" or "jspui"
|
||||||
@@ -145,6 +160,8 @@ public class SyndicationFeed
|
|||||||
String logoURL = null;
|
String logoURL = null;
|
||||||
String objectURL = null;
|
String objectURL = null;
|
||||||
String defaultTitle = null;
|
String defaultTitle = null;
|
||||||
|
boolean podcastFeed = false;
|
||||||
|
this.request = request;
|
||||||
|
|
||||||
// dso is null for the whole site, or a search without scope
|
// dso is null for the whole site, or a search without scope
|
||||||
if (dso == null)
|
if (dso == null)
|
||||||
@@ -163,6 +180,10 @@ public class SyndicationFeed
|
|||||||
defaultTitle = col.getMetadata("name");
|
defaultTitle = col.getMetadata("name");
|
||||||
feed.setDescription(col.getMetadata("short_description"));
|
feed.setDescription(col.getMetadata("short_description"));
|
||||||
logo = col.getLogo();
|
logo = col.getLogo();
|
||||||
|
String cols = ConfigurationManager.getProperty("webui.feed.podcast.collections");
|
||||||
|
if(cols != null && cols.length() > 1 && cols.contains(col.getHandle()) ) {
|
||||||
|
podcastFeed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (dso.getType() == Constants.COMMUNITY)
|
else if (dso.getType() == Constants.COMMUNITY)
|
||||||
{
|
{
|
||||||
@@ -170,6 +191,10 @@ public class SyndicationFeed
|
|||||||
defaultTitle = comm.getMetadata("name");
|
defaultTitle = comm.getMetadata("name");
|
||||||
feed.setDescription(comm.getMetadata("short_description"));
|
feed.setDescription(comm.getMetadata("short_description"));
|
||||||
logo = comm.getLogo();
|
logo = comm.getLogo();
|
||||||
|
String comms = ConfigurationManager.getProperty("webui.feed.podcast.communities");
|
||||||
|
if(comms != null && comms.length() > 1 && comms.contains(comm.getHandle()) ){
|
||||||
|
podcastFeed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
objectURL = resolveURL(request, dso);
|
objectURL = resolveURL(request, dso);
|
||||||
if (logo != null)
|
if (logo != null)
|
||||||
@@ -190,7 +215,11 @@ public class SyndicationFeed
|
|||||||
// be contained in the rdf. Not all RSS-viewers show this logo.
|
// be contained in the rdf. Not all RSS-viewers show this logo.
|
||||||
SyndImage image = new SyndImageImpl();
|
SyndImage image = new SyndImageImpl();
|
||||||
image.setLink(objectURL);
|
image.setLink(objectURL);
|
||||||
image.setTitle(localize(labels, MSG_LOGO_TITLE));
|
if (StringUtils.isNotBlank(feed.getTitle())) {
|
||||||
|
image.setTitle(feed.getTitle());
|
||||||
|
} else {
|
||||||
|
image.setTitle(localize(labels, MSG_LOGO_TITLE));
|
||||||
|
}
|
||||||
image.setUrl(logoURL);
|
image.setUrl(logoURL);
|
||||||
feed.setImage(image);
|
feed.setImage(image);
|
||||||
}
|
}
|
||||||
@@ -328,6 +357,79 @@ public class SyndicationFeed
|
|||||||
}
|
}
|
||||||
entry.getModules().add(dc);
|
entry.getModules().add(dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//iTunes Podcast Support - START
|
||||||
|
if (podcastFeed)
|
||||||
|
{
|
||||||
|
// Add enclosure(s)
|
||||||
|
List<SyndEnclosure> enclosures = new ArrayList();
|
||||||
|
try {
|
||||||
|
Bundle[] bunds = item.getBundles("ORIGINAL");
|
||||||
|
if (bunds[0] != null) {
|
||||||
|
Bitstream[] bits = bunds[0].getBitstreams();
|
||||||
|
for (int i = 0; (i < bits.length); i++) {
|
||||||
|
String mime = bits[i].getFormat().getMIMEType();
|
||||||
|
if(mime.contains("audio/x-mpeg")) {
|
||||||
|
SyndEnclosure enc = new SyndEnclosureImpl();
|
||||||
|
enc.setType(bits[i].getFormat().getMIMEType());
|
||||||
|
enc.setLength(bits[i].getSize());
|
||||||
|
enc.setUrl(urlOfBitstream(request, bits[i]));
|
||||||
|
enclosures.add(enc);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Also try to add an external value from dc.identifier.other
|
||||||
|
// We are assuming that if this is set, then it is a media file
|
||||||
|
DCValue[] externalMedia = item.getMetadata(sourceField);
|
||||||
|
if(externalMedia.length > 0)
|
||||||
|
{
|
||||||
|
for(int i = 0; i< externalMedia.length; i++)
|
||||||
|
{
|
||||||
|
SyndEnclosure enc = new SyndEnclosureImpl();
|
||||||
|
enc.setType("audio/x-mpeg");
|
||||||
|
enc.setLength(1);
|
||||||
|
enc.setUrl(externalMedia[i].value);
|
||||||
|
enclosures.add(enc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
entry.setEnclosures(enclosures);
|
||||||
|
|
||||||
|
// Get iTunes specific fields: author, subtitle, summary, duration, keywords
|
||||||
|
EntryInformation itunes = new EntryInformationImpl();
|
||||||
|
|
||||||
|
String author = getOneDC(item, authorField);
|
||||||
|
if (author != null && author.length() > 0) {
|
||||||
|
itunes.setAuthor(author); // <itunes:author>
|
||||||
|
}
|
||||||
|
|
||||||
|
itunes.setSubtitle(title == null ? localize(labels, MSG_UNTITLED) : title); // <itunes:subtitle>
|
||||||
|
|
||||||
|
if (db.length() > 0) {
|
||||||
|
itunes.setSummary(db.toString()); // <itunes:summary>
|
||||||
|
}
|
||||||
|
|
||||||
|
String extent = getOneDC(item, "dc.format.extent"); // assumed that user will enter this field with length of song in seconds
|
||||||
|
if (extent != null && extent.length() > 0) {
|
||||||
|
extent = extent.split(" ")[0];
|
||||||
|
Integer duration = Integer.parseInt(extent);
|
||||||
|
itunes.setDuration(new Duration(duration)); // <itunes:duration>
|
||||||
|
}
|
||||||
|
|
||||||
|
String subject = getOneDC(item, "dc.subject");
|
||||||
|
if (subject != null && subject.length() > 0) {
|
||||||
|
String[] subjects = new String[1];
|
||||||
|
subjects[0] = subject;
|
||||||
|
itunes.setKeywords(subjects); // <itunes:keywords>
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.getModules().add(itunes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
feed.setEntries(entries);
|
feed.setEntries(entries);
|
||||||
}
|
}
|
||||||
|
@@ -1388,6 +1388,20 @@ webui.feed.item.author = dc.contributor.author
|
|||||||
# Must be an absolute URL, e.g.
|
# Must be an absolute URL, e.g.
|
||||||
## webui.feed.logo.url = ${dspace.url}/themes/mysite/images/mysite-logo.png
|
## webui.feed.logo.url = ${dspace.url}/themes/mysite/images/mysite-logo.png
|
||||||
|
|
||||||
|
# iTunes Podcast Enhanced RSS Feed Properties
|
||||||
|
# Add all the communities / collections, separated by commas (no spaces) that should
|
||||||
|
# have the iTunes podcast metadata added to their RSS feed.
|
||||||
|
# Default: Disabled, No collections or communities have iTunes Podcast enhanced metadata in their feed.
|
||||||
|
# webui.feed.podcast.collections =123456789/2,123456789/3
|
||||||
|
# webui.feed.podcast.communities =123456789/1
|
||||||
|
|
||||||
|
# For the iTunes Podcast Feed, if you would like to specify an external media file,
|
||||||
|
# not on your DSpace server to be enclosed within the entry for each item,
|
||||||
|
# specify which metadata field will hold the URI to the external media file.
|
||||||
|
# This is useful if you store the metadata in DSpace, and a seperate streaming server to host the media.
|
||||||
|
# Default: dc.source.uri
|
||||||
|
#webui.feed.item.sourceuri = dc.source.uri
|
||||||
|
|
||||||
#### OpenSearch Settings ####
|
#### OpenSearch Settings ####
|
||||||
# NB: for result data formatting, OpenSearch uses Syndication Feed Settings
|
# NB: for result data formatting, OpenSearch uses Syndication Feed Settings
|
||||||
# so even if Syndication Feeds are not enabled, they must be configured
|
# so even if Syndication Feeds are not enabled, they must be configured
|
||||||
|
Reference in New Issue
Block a user