diff --git a/source/net/sourceforge/filebot/web/TMDbClient.java b/source/net/sourceforge/filebot/web/TMDbClient.java index a5a56f3b..75514059 100644 --- a/source/net/sourceforge/filebot/web/TMDbClient.java +++ b/source/net/sourceforge/filebot/web/TMDbClient.java @@ -2,17 +2,21 @@ package net.sourceforge.filebot.web; +import static java.util.Arrays.*; +import static java.util.Collections.*; import static net.sourceforge.filebot.web.WebRequest.*; import static net.sourceforge.tuned.XPathUtilities.*; import java.io.File; import java.io.IOException; +import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import java.util.Collections; +import java.util.EnumMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; @@ -23,6 +27,8 @@ import org.w3c.dom.Node; import org.xml.sax.SAXException; import net.sourceforge.filebot.ResourceManager; +import net.sourceforge.filebot.web.TMDbClient.Artwork.ArtworkProperty; +import net.sourceforge.filebot.web.TMDbClient.MovieInfo.MovieProperty; public class TMDbClient implements MovieIdentificationService { @@ -32,24 +38,24 @@ public class TMDbClient implements MovieIdentificationService { private final String apikey; - + public TMDbClient(String apikey) { this.apikey = apikey; } - + @Override public String getName() { return "TheMovieDB"; } - + @Override public Icon getIcon() { return ResourceManager.getIcon("search.themoviedb"); } - + @Override public List searchMovie(String query, Locale locale) throws IOException { try { @@ -57,21 +63,24 @@ public class TMDbClient implements MovieIdentificationService { } catch (SAXException e) { // TMDb output is sometimes malformed xml Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage()); - return Collections.emptyList(); + return emptyList(); } } - + public List searchMovie(File file, Locale locale) throws IOException, SAXException { + if (file.length() < OpenSubtitlesHasher.HASH_CHUNK_SIZE) + return emptyList(); + return searchMovie(OpenSubtitlesHasher.computeHash(file), file.length(), locale); } - + public List searchMovie(String hash, long bytesize, Locale locale) throws IOException, SAXException { return getMovies("Media.getInfo", hash + "/" + bytesize, locale); } - + @Override public Movie getMovieDescriptor(int imdbid, Locale locale) throws Exception { URL resource = getResource("Movie.imdbLookup", String.format("tt%07d", imdbid), locale); @@ -86,7 +95,7 @@ public class TMDbClient implements MovieIdentificationService { return new Movie(name, year, imdbid); } - + @Override public Movie[] getMovieDescriptors(File[] movieFiles, Locale locale) throws Exception { Movie[] movies = new Movie[movieFiles.length]; @@ -101,7 +110,7 @@ public class TMDbClient implements MovieIdentificationService { return movies; } - + protected List getMovies(String method, String parameter, Locale locale) throws IOException, SAXException { List result = new ArrayList(); @@ -124,10 +133,248 @@ public class TMDbClient implements MovieIdentificationService { return result; } - + protected URL getResource(String method, String parameter, Locale locale) throws MalformedURLException { // e.g. http://api.themoviedb.org/2.1/Movie.search/en/xml/{apikey}/serenity return new URL("http", host, "/" + version + "/" + method + "/" + locale.getLanguage() + "/xml/" + apikey + "/" + parameter); } + + public MovieInfo getMovieInfo(Movie movie, Locale locale) throws Exception { + URL resource = getResource("Movie.imdbLookup", String.format("tt%07d", movie.getImdbId()), locale); + Node node = selectNode("//movie", getDocument(resource)); + + Map movieProperties = new EnumMap(MovieProperty.class); + for (MovieProperty property : MovieProperty.values()) { + movieProperties.put(property, getTextContent(property.name(), node)); + } + + List genres = new ArrayList(); + for (Node category : selectNodes("//category[@type='genre']", node)) { + genres.add(getAttribute("name", category)); + } + + List artwork = new ArrayList(); + for (Node image : selectNodes("//image", node)) { + Map artworkProperties = new EnumMap(ArtworkProperty.class); + for (ArtworkProperty property : ArtworkProperty.values()) { + artworkProperties.put(property, getAttribute(property.name(), image)); + } + artwork.add(new Artwork(artworkProperties)); + } + + return new MovieInfo(movieProperties, genres, artwork); + } + + + public static class MovieInfo implements Serializable { + + public static enum MovieProperty { + translated, + adult, + language, + name, + type, + id, + imdb_id, + url, + overview, + votes, + rating, + certification, + released, + runtime + } + + + protected Map fields; + protected String[] genres; + protected Artwork[] images; + + + protected MovieInfo() { + // used by serializer + } + + + protected MovieInfo(Map fields, List genres, List images) { + this.fields = new EnumMap(fields); + this.genres = genres.toArray(new String[0]); + this.images = images.toArray(new Artwork[0]); + } + + + public String get(Object key) { + return fields.get(MovieProperty.valueOf(key.toString())); + } + + + public String get(MovieProperty key) { + return fields.get(key); + } + + + public boolean isTranslated() { + return Boolean.valueOf(get(MovieProperty.translated)); + } + + + public boolean isAdult() { + return Boolean.valueOf(get(MovieProperty.adult)); + } + + + public Locale getLanguage() { + return new Locale(get(MovieProperty.language)); + } + + + public String getName() { + return get(MovieProperty.name); + } + + + public String getType() { + return get(MovieProperty.type); + } + + + public int getId() { + return Integer.parseInt(get(MovieProperty.id)); + } + + + public int getImdbId() { + // e.g. tt0379786 + return Integer.parseInt(get(MovieProperty.imdb_id).substring(2)); + } + + + public URL getUrl() { + try { + return new URL(get(MovieProperty.url)); + } catch (Exception e) { + return null; + } + } + + + public String getOverview() { + return get(MovieProperty.overview); + } + + + public int getVotes() { + return Integer.parseInt(get(MovieProperty.votes)); + } + + + public double getRating() { + return Double.parseDouble(get(MovieProperty.rating)); + } + + + public String getCertification() { + // e.g. PG-13 + return get(MovieProperty.certification); + } + + + public Date getReleased() { + // e.g. 2005-09-30 + return Date.parse(get(MovieProperty.released), "yyyy-MM-dd"); + } + + + public int getRuntime() { + return Integer.parseInt(get(MovieProperty.runtime)); + } + + + public List getGenres() { + return unmodifiableList(asList(genres)); + } + + + public List getImages() { + return unmodifiableList(asList(images)); + } + + + @Override + public String toString() { + return fields.toString(); + } + } + + + public static class Artwork implements Serializable { + + public static enum ArtworkProperty { + type, + url, + size, + width, + height + } + + + protected Map fields; + + + protected Artwork() { + // used by serializer + } + + + protected Artwork(Map fields) { + this.fields = new EnumMap(fields); + } + + + public String get(Object key) { + return fields.get(ArtworkProperty.valueOf(key.toString())); + } + + + public String get(ArtworkProperty key) { + return fields.get(key); + } + + + public String getType() { + return get(ArtworkProperty.type); + } + + + public URL getUrl() { + try { + return new URL(get(ArtworkProperty.url)); + } catch (MalformedURLException e) { + return null; + } + } + + + public String getSize() { + return get(ArtworkProperty.size); + } + + + public int getWidth() { + return Integer.parseInt(get(ArtworkProperty.width)); + } + + + public int getHeight() { + return Integer.parseInt(get(ArtworkProperty.height)); + } + + + @Override + public String toString() { + return fields.toString(); + } + } + } diff --git a/test/net/sourceforge/filebot/web/TMDbClientTest.java b/test/net/sourceforge/filebot/web/TMDbClientTest.java index cd86a6fe..99fc14c6 100644 --- a/test/net/sourceforge/filebot/web/TMDbClientTest.java +++ b/test/net/sourceforge/filebot/web/TMDbClientTest.java @@ -10,12 +10,14 @@ import java.util.Locale; import org.junit.Test; +import net.sourceforge.filebot.web.TMDbClient.MovieInfo; + public class TMDbClientTest { private final TMDbClient tmdb = new TMDbClient(getApplicationProperty("themoviedb.apikey")); - + @Test public void searchByName() throws Exception { List result = tmdb.searchMovie("Serenity", Locale.CHINESE); @@ -26,25 +28,36 @@ public class TMDbClientTest { assertEquals(379786, movie.getImdbId()); } - + @Test public void searchByHash() throws Exception { List results = tmdb.searchMovie("907172e7fe51ba57", 742086656, Locale.ENGLISH); Movie movie = results.get(0); assertEquals("Sin City", movie.getName()); - assertEquals(2005, movie.getYear()); - assertEquals(401792, movie.getImdbId()); + assertEquals(2005, movie.getYear(), 0); + assertEquals(401792, movie.getImdbId(), 0); } - + @Test public void searchByIMDB() throws Exception { Movie movie = tmdb.getMovieDescriptor(418279, Locale.ENGLISH); assertEquals("Transformers", movie.getName()); - assertEquals(2007, movie.getYear()); - assertEquals(418279, movie.getImdbId()); + assertEquals(2007, movie.getYear(), 0); + assertEquals(418279, movie.getImdbId(), 0); } + + @Test + public void getMovieInfo() throws Exception { + MovieInfo movie = tmdb.getMovieInfo(new Movie(null, 0, 418279), Locale.ENGLISH); + + assertEquals("Transformers", movie.getName()); + assertEquals("2007-07-03", movie.getReleased().toString()); + assertEquals("Adventure", movie.getGenres().get(0)); + assertEquals("thumb", movie.getImages().get(0).getSize()); + assertEquals("http://cf2.imgobject.com/t/p/w92/bgSHbGEA1OM6qDs3Qba4VlSZsNG.jpg", movie.getImages().get(0).getUrl().toString()); + } }