diff --git a/fw/search.imdb.png b/fw/search.imdb.png index aab7bc8a..d7adccad 100644 Binary files a/fw/search.imdb.png and b/fw/search.imdb.png differ diff --git a/source/net/sourceforge/filebot/resources/search.imdb.png b/source/net/sourceforge/filebot/resources/search.imdb.png new file mode 100644 index 00000000..7d116c7c Binary files /dev/null and b/source/net/sourceforge/filebot/resources/search.imdb.png differ diff --git a/source/net/sourceforge/filebot/similarity/SeasonEpisodeMatcher.java b/source/net/sourceforge/filebot/similarity/SeasonEpisodeMatcher.java index f29d6239..1591c26b 100644 --- a/source/net/sourceforge/filebot/similarity/SeasonEpisodeMatcher.java +++ b/source/net/sourceforge/filebot/similarity/SeasonEpisodeMatcher.java @@ -16,10 +16,10 @@ public class SeasonEpisodeMatcher { public SeasonEpisodeMatcher() { patterns = new SeasonEpisodePattern[3]; - // match patterns like S01E01, s01e02, ... [s01]_[e02], s01.e02, ... + // match patterns like S01E01, s01e02, ... [s01]_[e02], s01.e02, s01e02a, ... patterns[0] = new SeasonEpisodePattern("(? search(String query) throws IOException, SAXException { + + URL searchUrl = new URL("http", host, "/find?s=tt&q=" + URLEncoder.encode(query, "UTF-8")); + + Document dom = getHtmlDocument(openConnection(searchUrl)); + + List nodes = selectNodes("//TABLE//A[following-sibling::SMALL[contains(.,'TV series')]]", dom); + + List results = new ArrayList(nodes.size()); + + for (Node node : nodes) { + String name = removeQuotationMarks(node.getTextContent().trim()); + String year = node.getNextSibling().getTextContent().trim(); + String href = getAttribute("href", node); + + String nameAndYear = String.format("%s %s", name, year).trim(); + int imdbId = new Scanner(href).useDelimiter("\\D+").nextInt(); + + results.add(new MovieDescriptor(nameAndYear, imdbId)); + } + + return results; + } + + + @Override + public List getEpisodeList(SearchResult searchResult) throws IOException, SAXException { + Document dom = getHtmlDocument(openConnection(getEpisodeListLink(searchResult).toURL())); + + String seriesName = removeQuotationMarks(selectString("//H1/A", dom)); + + List nodes = selectNodes("//TABLE//H3/A[preceding-sibling::text()]", dom); + + List episodes = new ArrayList(nodes.size()); + + for (Node node : nodes) { + String title = node.getTextContent().trim(); + + Scanner numberScanner = new Scanner(node.getPreviousSibling().getTextContent()).useDelimiter("\\D+"); + String season = numberScanner.next(); + String episode = numberScanner.next(); + + episodes.add(new Episode(seriesName, season, episode, title)); + } + + return episodes; + } + + + @Override + public List getEpisodeList(SearchResult searchResult, int season) throws Exception { + + List episodes = new ArrayList(25); + + // remember max. season, so we can throw a proper exception, in case an illegal season number was requested + int maxSeason = 0; + + // filter given season from all seasons + for (Episode episode : getEpisodeList(searchResult)) { + try { + int seasonNumber = Integer.parseInt(episode.getSeasonNumber()); + + if (season == seasonNumber) { + episodes.add(episode); + } + + if (seasonNumber > maxSeason) { + maxSeason = seasonNumber; + } + } catch (NumberFormatException e) { + Logger.getLogger(getClass().getName()).log(Level.WARNING, "Illegal season number", e); + } + } + + if (episodes.isEmpty()) { + throw new SeasonOutOfBoundsException(searchResult.getName(), season, maxSeason); + } + + return episodes; + } + + + protected URLConnection openConnection(URL url) throws IOException { + URLConnection connection = url.openConnection(); + + // IMDb refuses default user agent (Java/1.6.0_12) + connection.addRequestProperty("User-Agent", "Scraper"); + + return connection; + } + + + protected String removeQuotationMarks(String name) { + return name.replaceAll("^\"|\"$", ""); + } + + + @Override + public URI getEpisodeListLink(SearchResult searchResult) { + return URI.create("http://" + host + String.format("/title/tt%07d/episodes", ((MovieDescriptor) searchResult).getImdbId())); + } + + + @Override + public URI getEpisodeListLink(SearchResult searchResult, int season) { + return null; + } + +} diff --git a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java index 81d7dbf5..23864322 100644 --- a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java @@ -117,7 +117,7 @@ public class SubsceneSubtitleClient implements SubtitleClient { Document subtitleListDocument = getSubtitleListDocument(subtitleListUrl, languageFilter); // let's update language filters if they are not known yet - if (languageFilterMap.isEmpty()) { + if (languageName != null && languageFilter == null) { synchronized (languageFilterMap) { languageFilterMap.putAll(getLanguageFilterMap(subtitleListDocument)); } diff --git a/source/net/sourceforge/filebot/web/TheTVDBClient.java b/source/net/sourceforge/filebot/web/TheTVDBClient.java index 624a4584..9974b6c3 100644 --- a/source/net/sourceforge/filebot/web/TheTVDBClient.java +++ b/source/net/sourceforge/filebot/web/TheTVDBClient.java @@ -10,7 +10,6 @@ import static net.sourceforge.tuned.XPathUtilities.selectString; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; @@ -205,11 +204,7 @@ public class TheTVDBClient implements EpisodeListClient { public URI getEpisodeListLink(SearchResult searchResult) { int seriesId = ((TheTVDBSearchResult) searchResult).getSeriesId(); - try { - return new URI("http://" + host + "/?tab=seasonall&id=" + seriesId); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } + return URI.create("http://" + host + "/?tab=seasonall&id=" + seriesId); } @@ -230,11 +225,9 @@ public class TheTVDBClient implements EpisodeListClient { } return new URI("http://" + host + "/?tab=season&seriesid=" + seriesId + "&seasonid=" + seasonId); - } catch (IOException e) { + } catch (Exception e) { // log and ignore any IOException Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to retrieve season id", e); - } catch (Exception e) { - throw new RuntimeException(e); } return null; diff --git a/test/net/sourceforge/filebot/web/IMDbClientTest.java b/test/net/sourceforge/filebot/web/IMDbClientTest.java new file mode 100644 index 00000000..4a8bc1d0 --- /dev/null +++ b/test/net/sourceforge/filebot/web/IMDbClientTest.java @@ -0,0 +1,79 @@ + +package net.sourceforge.filebot.web; + + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + + +public class IMDbClientTest { + + private final IMDbClient imdb = new IMDbClient(); + + + @Test + public void search() throws Exception { + List results = imdb.search("battlestar"); + + MovieDescriptor movie = (MovieDescriptor) results.get(0); + + assertEquals("Battlestar Galactica (2004)", movie.getName()); + assertEquals(407362, movie.getImdbId(), 0); + + assertEquals(6, results.size(), 0); + } + + + @Test + public void getEpisodeList() throws Exception { + List list = imdb.getEpisodeList(new MovieDescriptor("Buffy", 118276)); + + assertEquals(145, list.size()); + + Episode first = list.get(0); + + assertEquals("Buffy the Vampire Slayer", first.getSeriesName()); + assertEquals("Unaired Pilot", first.getTitle()); + assertEquals("0", first.getEpisodeNumber()); + assertEquals("1", first.getSeasonNumber()); + + Episode last = list.get(144); + + assertEquals("Buffy the Vampire Slayer", last.getSeriesName()); + assertEquals("Chosen", last.getTitle()); + assertEquals("22", last.getEpisodeNumber()); + assertEquals("7", last.getSeasonNumber()); + } + + + @Test + public void getEpisodeListWithUnknownSeason() throws Exception { + List list = imdb.getEpisodeList(new MovieDescriptor("Mushishi", 807832)); + + assertEquals(26, list.size()); + + Episode first = list.get(0); + + assertEquals("Mushishi", first.getSeriesName()); + assertEquals("Midori no za", first.getTitle()); + assertEquals("1", first.getEpisodeNumber()); + assertEquals("1", first.getSeasonNumber()); + } + + + @Test + public void getEpisodeListLink() throws Exception { + assertEquals("http://www.imdb.com/title/tt0407362/episodes", imdb.getEpisodeListLink(new MovieDescriptor("Battlestar Galactica", 407362)).toString()); + } + + + @Test + public void removeQuotationMarks() throws Exception { + assertEquals("test", imdb.removeQuotationMarks("\"test\"")); + + assertEquals("inner \"quotation marks\"", imdb.removeQuotationMarks("\"inner \"quotation marks\"\"")); + } +} diff --git a/test/net/sourceforge/filebot/web/WebTestSuite.java b/test/net/sourceforge/filebot/web/WebTestSuite.java index 46c885a2..37993dc0 100644 --- a/test/net/sourceforge/filebot/web/WebTestSuite.java +++ b/test/net/sourceforge/filebot/web/WebTestSuite.java @@ -8,7 +8,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses( { TVDotComClientTest.class, AnidbClientTest.class, TVRageClientTest.class, TheTVDBClientTest.class, SubsceneSubtitleClientTest.class, SubtitleSourceClientTest.class, OpenSubtitlesHasherTest.class }) +@SuiteClasses( { TVDotComClientTest.class, AnidbClientTest.class, TVRageClientTest.class, TheTVDBClientTest.class, IMDbClientTest.class, SubsceneSubtitleClientTest.class, SubtitleSourceClientTest.class, OpenSubtitlesHasherTest.class }) public class WebTestSuite { }