/*
* Copyright (2012) Schibsted ASA
* This file is part of Possom.
*
* Possom is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Possom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Possom. If not, see <http://www.gnu.org/licenses/>.
*/
package no.sesat.search.mode.command;
import java.io.IOException;
import java.io.Serializable;
import java.net.SocketTimeoutException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import no.sesat.search.mode.config.YoutubeCommandConfig;
import no.sesat.search.result.BasicResultItem;
import no.sesat.search.result.BasicResultList;
import no.sesat.search.result.ResultItem;
import no.sesat.search.result.ResultList;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* @see YoutubeCommandConfig
* @version $Id$
*/
public class YoutubeSearchCommand extends AbstractXmlSearchCommand {
// Constants -----------------------------------------------------
private static final Logger LOG = Logger.getLogger(YoutubeSearchCommand.class);
private static final String COMMAND_URL_PATTERN =
"/feeds/api/videos?vq={0}&orderby={1}&start-index={2}&max-results={3}"
+ "&format={4}&racy={5}";
// Static --------------------------------------------------------
// Attributes ----------------------------------------------------
private final Context cxt;
private final YoutubeCommandConfig conf;
// Constructors -------------------------------------------------
public YoutubeSearchCommand(final Context cxt) {
super(cxt);
this.cxt = cxt;
conf = (YoutubeCommandConfig) cxt.getSearchConfiguration();
setXmlRestful(
new AbstractXmlRestful(cxt) {
public String createRequestURL() {
return MessageFormat.format(COMMAND_URL_PATTERN,
cxt.getDataModel().getQuery().getUtf8UrlEncoded(),
(null != getParameter("userSortBy")
? getParameter("userSortBy")
: conf.getSortBy()),
// first result is 1, not 0
(null != YoutubeSearchCommand.this.getParameter("offset")
? Integer.parseInt(YoutubeSearchCommand.this.getParameter("offset"))+1
: "1"),
conf.getResultsToReturn(),
conf.getFormat(),
conf.getRacy());
}
});
}
// Public --------------------------------------------------------
@Override
@SuppressWarnings("static-access")
public ResultList<ResultItem> execute() {
final ResultList<ResultItem> result = new BasicResultList<ResultItem>();
Document doc;
try {
doc = getXmlRestful().getXmlResult();
} catch (SocketTimeoutException ste) {
LOG.error(getSearchConfiguration().getName() + " --> " + ste.getMessage());
return new BasicResultList<ResultItem>();
} catch (IOException ex) {
throw new SearchCommandException(ex);
} catch (SAXException ex) {
throw new SearchCommandException(ex);
}
if(null != doc) {
final Element rootEl = doc.getDocumentElement();
final Element totalResultsEl = (Element) rootEl.getElementsByTagName("openSearch:totalResults").item(0);
final String totalResults = totalResultsEl.getFirstChild().getNodeValue();
result.setHitCount(Integer.parseInt(totalResults));
final NodeList entryList = rootEl.getElementsByTagName("entry");
for(int i = 0; i < entryList.getLength(); i++) {
result.addResult(createItem((Element) entryList.item(i)));
}
}
return result;
}
// Protected --------------------------------------------------------
@Override
protected ResultItem createItem(final Element entryEl) {
ResultItem resItem = new BasicResultItem();
final Element publishedEl = (Element) entryEl.getElementsByTagName("published").item(0);
final String published = publishedEl.getFirstChild().getNodeValue();
final NodeList categoryList = entryEl.getElementsByTagName("category");
List<String> tags = new ArrayList<String>();
List<String> categories = new ArrayList<String>();
for(int j = 0; j < categoryList.getLength(); j++) {
final Element categoryEl = (Element) categoryList.item(j);
final String term = categoryEl.getAttribute("term");
final String label = categoryEl.getAttribute("label");
if(null != label && label.length() > 0) {
categories.add(term);
} else if(!term.startsWith("http")) { // Filter out junk
tags.add(term);
}
}
final Element titleEl = (Element) entryEl.getElementsByTagName("title").item(0);
final String title = titleEl.getFirstChild().getNodeValue();
final Element contentEl = (Element) entryEl.getElementsByTagName("content").item(0);
final String content = contentEl.getFirstChild().getNodeValue();
final Element authorEl = (Element) entryEl.getElementsByTagName("author").item(0);
final Element authorNameEl = (Element) authorEl.getElementsByTagName("name").item(0);
final Element authorUrlEl = (Element) authorEl.getElementsByTagName("uri").item(0);
final String author = authorNameEl.getFirstChild().getNodeValue();
final String authorUrl = authorUrlEl.getFirstChild().getNodeValue();
final Element mediaGroupEl = (Element) entryEl.getElementsByTagName("media:group").item(0);
final Element durationEl = (Element) mediaGroupEl.getElementsByTagName("yt:duration").item(0);
final String duration = durationEl.getAttribute("seconds");
final String durationMin = String.valueOf(Integer.parseInt(duration) / 60);
final String durationSec = String.valueOf(Integer.parseInt(duration) % 60);
final NodeList mediaContentList = mediaGroupEl.getElementsByTagName("media:content");
for(int j = 0; j < mediaContentList.getLength(); j++) {
final Element mediaContentEl = (Element) mediaContentList.item(j);
final String url = mediaContentEl.getAttribute("url");
final String format = mediaContentEl.getAttribute("yt:format");
final String type = mediaContentEl.getAttribute("type");
if(format.equals(conf.getFormat())) {
resItem = resItem
.addField("videoUrl", url)
.addField("videoType", type);
}
}
final Element mediaPlayerEl = (Element) mediaGroupEl.getElementsByTagName("media:player").item(0);
final String playerUrl = mediaPlayerEl.getAttribute("url");
final List<Map<String,String>> thumbnails = new ArrayList<Map<String,String>>();
final NodeList mediaThumbnailList = mediaGroupEl.getElementsByTagName("media:thumbnail");
for(int j = 0; j < mediaThumbnailList.getLength(); j++) {
final Element mediaThumbnailEl = (Element) mediaThumbnailList.item(j);
final String url = mediaThumbnailEl.getAttribute("url");
final String height = mediaThumbnailEl.getAttribute("height");
final String width = mediaThumbnailEl.getAttribute("width");
final String time = mediaThumbnailEl.getAttribute("time");
final Map<String,String> thumbnail = new HashMap<String,String>();
thumbnail.put("url", url);
thumbnail.put("height", height);
thumbnail.put("width", width);
thumbnail.put("time", time);
int lastDot = url.lastIndexOf(".");
thumbnail.put("id", url.substring(lastDot-1, lastDot));
thumbnails.add(thumbnail);
}
final Element gdRating = (Element)entryEl.getElementsByTagName("gd:rating").item(0);
String ratingNum = null;
String rating = null;
if(null != gdRating) {
ratingNum = gdRating.getAttribute("numRaters");
rating = gdRating.getAttribute("average");
}
return resItem
.addField("title", title)
.addField("content", content)
.addField("published", published)
.addField("author", author)
.addField("authorUrl", authorUrl)
.addField("duration", duration)
.addField("durationMin", durationMin)
.addField("durationSec", durationSec)
.addObjectField("thumbnails",(Serializable) thumbnails)
.addObjectField("categories",(Serializable) categories)
.addObjectField("tags",(Serializable) tags)
.addField("ratingNum", ratingNum)
.addField("rating", rating)
.addField("youtubeUrl", playerUrl);
}
@Override
protected String getParameter(String paramName) {
return super.getParameter(paramName);
}
// Private --------------------------------------------------------
}