/*
* This file is part of Flicklib.
*
* Copyright (C) Francis De Brabandere
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.flicklib.service.movie.netflix;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
import net.oauth.OAuthServiceProvider;
import net.oauth.client.HttpClientPool;
import net.oauth.client.OAuthClient;
import net.oauth.client.OAuthHttpClient;
import org.apache.commons.httpclient.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import com.flicklib.api.AbstractMovieInfoFetcher;
import com.flicklib.domain.MoviePage;
import com.flicklib.domain.MovieSearchResult;
import com.flicklib.domain.MovieService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
/**
* @author francisdb
*
*/
@Singleton
public class NetflixInfoFetcher extends AbstractMovieInfoFetcher {
private static final Logger LOGGER = LoggerFactory.getLogger(NetflixInfoFetcher.class);
/**
* http://www.netflix.com
*/
final static MovieService NETFLIX = new MovieService("NETFLIX", "Netflix", "http://www.netflix.com");
private final OAuthAccessor accessor;
@Inject
public NetflixInfoFetcher(@Named("netflix.key") final String apikey, @Named("netflix.secret") final String sharedsecret) {
OAuthServiceProvider provider = new OAuthServiceProvider(
"http://api.netflix.com/oauth/request_token",
"https://api-user.netflix.com/oauth/login",
"http://api.netflix.com/oauth/access_token");
OAuthConsumer consumer = new OAuthConsumer(null, apikey, sharedsecret, provider);
accessor = new OAuthAccessor(consumer);
}
@Override
public MoviePage getMovieInfo(String id) throws IOException {
throw new RuntimeException("Not implemented");
}
@Override
public List<? extends MovieSearchResult> search(String title) throws IOException {
List<? extends MovieSearchResult> result = null;
OAuthClient oAuthClient = new OAuthHttpClient(new NotPoolingHttpClientPool());
Map<String, String> params = new HashMap<String, String>();
params.put("term", title);
params.put("max_results", String.valueOf(20));
try {
OAuthMessage message = oAuthClient.invoke(accessor, "GET", "http://api.netflix.com/catalog/titles", params.entrySet());
// LOGGER.info(message.getBodyAsString());
SaxResultUnmarshaller saxUms = new SaxResultUnmarshaller();
XMLReader rdr = XMLReaderFactory.createXMLReader();
rdr.setContentHandler(saxUms);
rdr.parse(new InputSource(message.getBodyAsStream()));
result = saxUms.getResult();
} catch (OAuthException e) {
throw new IOException("Problem while requesting data from netflix", e);
} catch (URISyntaxException e) {
throw new IOException("Problem while requesting data from netflix", e);
} catch (SAXException e) {
throw new IOException("Problem while requesting data from netflix", e);
}
return result;
}
/**
* TODO Should be removed when switching to new OAuth version (is
* implemented overthere)
*
* @author francisdb
*
*/
private static final class NotPoolingHttpClientPool implements HttpClientPool {
@Override
public HttpClient getHttpClient(URL server) {
return new HttpClient();
}
}
/**
* Parses a single netflix xml result
*
* @author francisdb
*
*/
private static class SaxResultUnmarshaller extends DefaultHandler {
/**
* Rating in %
*/
private MoviePage moviePage;
private String tagToReadContentFrom;
private final List<MoviePage> result;
public SaxResultUnmarshaller() {
result = new ArrayList<MoviePage>();
moviePage = new MoviePage(NETFLIX);
}
public List<MoviePage> getResult() {
return result;
}
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
LOGGER.debug("element <"+name+" "+toString(attributes)+">");
if ("catalog_title".equals(name)) {
moviePage = new MoviePage(NETFLIX);
}
if ("id".equals(name)) {
tagToReadContentFrom = name;
} else if ("link".equals(name)) {
String rel = attributes.getValue("rel");
if ("alternate".equals(rel)) {
moviePage.setUrl(attributes.getValue("href"));
}
} else if ("average_rating".equals(name)) {
tagToReadContentFrom = name;
} else if ("box_art".equals(name)) {
moviePage.setImgUrl(attributes.getValue("large"));
} else if ("release_year".equals(name)) {
tagToReadContentFrom = name;
} else if ("title".equals(name)) {
moviePage.setTitle(attributes.getValue("regular"));
} else if ("category".equals(name)) {
String scheme = attributes.getValue("scheme");
if ("http://api.netflix.com/categories/genres".equals(scheme)) {
moviePage.addGenre(attributes.getValue("label"));
}
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (tagToReadContentFrom != null) {
String txt = new String(ch, start, length);
if ("average_rating".equals(tagToReadContentFrom)) {
moviePage.setScore((int) (Double.valueOf(txt) * 20));
} else if ("id".equals(tagToReadContentFrom)) {
moviePage.setIdForSite(txt);
} else if ("release_year".equals(tagToReadContentFrom)) {
moviePage.setYear(Integer.parseInt(txt));
}
}
}
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if ("catalog_title".equals(name)) {
result.add(moviePage);
moviePage = null;
}
tagToReadContentFrom = null;
}
String toString(Attributes attributes) {
StringBuilder s = new StringBuilder();
for (int i=0;i<attributes.getLength();i++) {
s.append(attributes.getLocalName(i)).append("='").append(attributes.getValue(i)).append("' ");
}
return s.toString();
}
}
@Override
public MovieService getService() {
return NETFLIX;
}
}