From 5519c1440d475a2c9d8f4b93402e29727dcbd04a Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Sun, 22 Nov 2009 01:27:05 +0000 Subject: [PATCH] * added fallback for movie identification by scanning .nfo files for an imdb id --- source/net/sourceforge/filebot/media.types | 6 +- .../ui/panel/rename/MovieHashMatcher.java | 86 +++++++++++++++++++ .../web/MovieIdentificationService.java | 4 + .../filebot/web/OpenSubtitlesClient.java | 9 ++ .../filebot/web/OpenSubtitlesXmlRpc.java | 19 ++++ .../sourceforge/filebot/web/TMDbClient.java | 14 +++ .../filebot/web/OpenSubtitlesXmlRpcTest.java | 20 +++++ .../filebot/web/TMDbClientTest.java | 10 +++ 8 files changed, 167 insertions(+), 1 deletion(-) diff --git a/source/net/sourceforge/filebot/media.types b/source/net/sourceforge/filebot/media.types index 0c344356..6e4443d4 100644 --- a/source/net/sourceforge/filebot/media.types +++ b/source/net/sourceforge/filebot/media.types @@ -13,7 +13,11 @@ txt - + + nfo + + + diff --git a/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java b/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java index 09f54106..b6777912 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java @@ -3,12 +3,24 @@ package net.sourceforge.filebot.ui.panel.rename; import static net.sourceforge.filebot.MediaTypes.*; +import static net.sourceforge.tuned.ui.TunedUtilities.*; import java.io.File; +import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Scanner; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; + +import javax.swing.Action; +import javax.swing.SwingUtilities; import net.sourceforge.filebot.similarity.Match; +import net.sourceforge.filebot.ui.SelectDialog; import net.sourceforge.filebot.web.MovieDescriptor; import net.sourceforge.filebot.web.MovieIdentificationService; import net.sourceforge.tuned.FileUtilities; @@ -35,10 +47,84 @@ class MovieHashMatcher implements AutoCompleteMatcher { for (int i = 0; i < movieDescriptors.length; i++) { if (movieDescriptors[i] != null) { matches.add(new Match(movieFiles[i], movieDescriptors[i])); + } else { + // unknown hash, try via imdb id from nfo file + MovieDescriptor movie = determineMovie(movieFiles[i]); + + if (movie != null) { + matches.add(new Match(movieFiles[i], movie)); + } } } return matches; } + + protected Set grepImdbId(File... files) throws IOException { + Set collection = new HashSet(); + + for (File file : files) { + Scanner scanner = new Scanner(file); + String imdb = null; + + // scan for imdb id patterns like tt1234567 + while ((imdb = scanner.findWithinHorizon("(?<=tt)\\d{7}", 32 * 1024)) != null) { + collection.add(Integer.parseInt(imdb)); + } + } + + return collection; + } + + + protected MovieDescriptor determineMovie(File movieFile) throws Exception { + List options = new ArrayList(); + + for (int imdbid : grepImdbId(movieFile.getParentFile().listFiles(getDefaultFilter("application/nfo")))) { + MovieDescriptor movie = service.getMovieDescriptor(imdbid); + + if (movie != null) { + options.add(movie); + } + } + + return options.isEmpty() ? null : selectMovie(options); + } + + + protected MovieDescriptor selectMovie(final List options) throws Exception { + if (options.size() == 1) { + return options.get(0); + } + + // show selection dialog on EDT + final RunnableFuture showSelectDialog = new FutureTask(new Callable() { + + @Override + public MovieDescriptor call() throws Exception { + // multiple results have been found, user must select one + SelectDialog selectDialog = new SelectDialog(null, options); + + selectDialog.getHeaderLabel().setText("Select Movie:"); + selectDialog.getCancelAction().putValue(Action.NAME, "Ignore"); + + // show dialog + selectDialog.setLocation(getOffsetLocation(selectDialog.getOwner())); + selectDialog.setVisible(true); + + // selected value or null if the dialog was canceled by the user + return selectDialog.getSelectedValue(); + } + }); + + // allow only one select dialog at a time + synchronized (this) { + SwingUtilities.invokeAndWait(showSelectDialog); + } + + // selected value or null + return showSelectDialog.get(); + } + } diff --git a/source/net/sourceforge/filebot/web/MovieIdentificationService.java b/source/net/sourceforge/filebot/web/MovieIdentificationService.java index 4523d60d..3ba6f13f 100644 --- a/source/net/sourceforge/filebot/web/MovieIdentificationService.java +++ b/source/net/sourceforge/filebot/web/MovieIdentificationService.java @@ -7,5 +7,9 @@ import java.io.File; public interface MovieIdentificationService { + public MovieDescriptor getMovieDescriptor(int imdbid) throws Exception; + + public MovieDescriptor[] getMovieDescriptors(File[] movieFiles) throws Exception; + } diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java b/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java index 84c9e1df..4199dc07 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java @@ -146,6 +146,15 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS } + @Override + public MovieDescriptor getMovieDescriptor(int imdbid) throws Exception { + // require login + login(); + + return xmlrpc.getIMDBMovieDetails(imdbid); + } + + @Override public MovieDescriptor[] getMovieDescriptors(File[] movieFiles) throws Exception { // create result array diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java b/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java index 58471f5b..1ffaa1b3 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java @@ -243,6 +243,25 @@ public class OpenSubtitlesXmlRpc { } + @SuppressWarnings("unchecked") + public MovieDescriptor getIMDBMovieDetails(int imdbid) throws XmlRpcFault { + Map response = invoke("GetIMDBMovieDetails", token, imdbid); + + try { + Map data = (Map) response.get("data"); + + String name = data.get("title"); + int year = Integer.parseInt(data.get("year")); + + return new MovieDescriptor(name, year, imdbid); + } catch (RuntimeException e) { + // ignore, invalid response + } + + return null; + } + + @SuppressWarnings("unchecked") public Map getSubLanguages(String languageCode) throws XmlRpcFault { Map>> response = (Map>>) invoke("GetSubLanguages", languageCode); diff --git a/source/net/sourceforge/filebot/web/TMDbClient.java b/source/net/sourceforge/filebot/web/TMDbClient.java index 63c23dd6..fc1b922d 100644 --- a/source/net/sourceforge/filebot/web/TMDbClient.java +++ b/source/net/sourceforge/filebot/web/TMDbClient.java @@ -54,6 +54,20 @@ public class TMDbClient implements MovieIdentificationService { } + public MovieDescriptor getMovieDescriptor(int imdbid) throws Exception { + URL resource = getResource("Movie.imdbLookup", String.format("tt%07d", imdbid)); + Node movie = selectNode("//movie", getDocument(resource)); + + if (movie == null) + return null; + + String name = getTextContent("name", movie); + int year = new Scanner(getTextContent("released", movie)).useDelimiter("\\D+").nextInt(); + + return new MovieDescriptor(name, year, imdbid); + } + + @Override public MovieDescriptor[] getMovieDescriptors(File[] movieFiles) throws Exception { MovieDescriptor[] movies = new MovieDescriptor[movieFiles.length]; diff --git a/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java b/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java index ddf8272c..48560888 100644 --- a/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java +++ b/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java @@ -140,6 +140,26 @@ public class OpenSubtitlesXmlRpcTest { } + @Test + public void getIMDBMovieDetails() throws Exception { + MovieDescriptor movie = xmlrpc.getIMDBMovieDetails(371746); + + assertEquals("Iron Man", movie.getName()); + assertEquals(2008, movie.getYear()); + assertEquals(371746, movie.getImdbId()); + } + + + @Test + public void getIMDBMovieDetailsInvalid() throws Exception { + MovieDescriptor movie = xmlrpc.getIMDBMovieDetails(371746); + + assertEquals("Iron Man", movie.getName()); + assertEquals(2008, movie.getYear()); + assertEquals(371746, movie.getImdbId()); + } + + @Test public void detectLanguage() throws Exception { String text = "Only those that are prepared to fire should be fired at."; diff --git a/test/net/sourceforge/filebot/web/TMDbClientTest.java b/test/net/sourceforge/filebot/web/TMDbClientTest.java index da027f46..6530eb49 100644 --- a/test/net/sourceforge/filebot/web/TMDbClientTest.java +++ b/test/net/sourceforge/filebot/web/TMDbClientTest.java @@ -36,4 +36,14 @@ public class TMDbClientTest { assertEquals(371746, movie.getImdbId()); } + + @Test + public void searchByIMDB() throws Exception { + MovieDescriptor movie = tmdb.getMovieDescriptor(418279); + + assertEquals("Transformers", movie.getName()); + assertEquals(2007, movie.getYear()); + assertEquals(418279, movie.getImdbId()); + } + }