From cd09a67c5e41081675e321bc47947240c4d57bef Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Sun, 6 Jul 2008 03:17:23 +0000 Subject: [PATCH] * heavily improved TV.com Client (better results, 2x - 4x faster) * improved EpisodeListClient API * unittests --- .../sourceforge/filebot/web/AnidbClient.java | 46 +++++-- .../filebot/web/EpisodeListClient.java | 34 ++--- .../sourceforge/filebot/web/HyperLink.java | 16 +-- .../filebot/web/SubsceneSubtitleClient.java | 8 +- .../sourceforge/filebot/web/TVRageClient.java | 129 +++++++++++++----- .../filebot/web/TVDotComClientTest.java | 97 +++++++++++++ .../filebot/web/TVRageClientTest.java | 15 +- .../sourceforge/filebot/web/WebTestSuite.java | 2 +- 8 files changed, 268 insertions(+), 79 deletions(-) create mode 100644 test/net/sourceforge/filebot/web/TVDotComClientTest.java diff --git a/source/net/sourceforge/filebot/web/AnidbClient.java b/source/net/sourceforge/filebot/web/AnidbClient.java index cceb8716..2406dabb 100644 --- a/source/net/sourceforge/filebot/web/AnidbClient.java +++ b/source/net/sourceforge/filebot/web/AnidbClient.java @@ -6,10 +6,12 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -32,7 +34,7 @@ public class AnidbClient extends EpisodeListClient { public AnidbClient() { - super("AniDB", ResourceManager.getIcon("search.anidb"), false); + super("AniDB", ResourceManager.getIcon("search.anidb")); }; @@ -48,24 +50,24 @@ public class AnidbClient extends EpisodeListClient { List searchResults = new ArrayList(nodes.size()); - if (!nodes.isEmpty()) + if (!nodes.isEmpty()) { for (Node node : nodes) { Node titleNode = XPathUtil.selectNode("./TD[@class='name']/A", node); String title = XPathUtil.selectString(".", titleNode); String href = XPathUtil.selectString("@href", titleNode); - String file = "/perl-bin/" + href; + String path = "/perl-bin/" + href; try { - URL url = new URL("http", host, file); + URI animeUrl = new URI("http", host, path, null); - searchResults.add(new HyperLink(title, url)); - } catch (MalformedURLException e) { + searchResults.add(new HyperLink(title, animeUrl)); + } catch (URISyntaxException e) { Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid href: " + href); } } - else { + } else { // we might have been redirected to the episode list page directly List list = XPathUtil.selectNodes("//TABLE[@class='eplist']", dom); @@ -74,7 +76,7 @@ public class AnidbClient extends EpisodeListClient { String header = XPathUtil.selectString("id('layout-content')//H1[1]", dom); String title = header.replaceFirst("Anime:\\s*", ""); - searchResults.add(new HyperLink(title, getSearchUrl(searchterm))); + searchResults.add(new HyperLink(title, URI.create(getSearchUrl(searchterm).toString()))); } } @@ -85,9 +87,9 @@ public class AnidbClient extends EpisodeListClient { @Override - public List getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException { + public List getEpisodeList(SearchResult searchResult) throws IOException, SAXException { - Document dom = HtmlUtil.getHtmlDocument(getEpisodeListLink(searchResult, season)); + Document dom = HtmlUtil.getHtmlDocument(getEpisodeListLink(searchResult)); List nodes = XPathUtil.selectNodes("id('eplist')//TR/TD/SPAN/ancestor::TR", dom); @@ -119,9 +121,27 @@ public class AnidbClient extends EpisodeListClient { } + @Override + public URI getEpisodeListLink(SearchResult searchResult) { + return ((HyperLink) searchResult).getURI(); + } + + + @Override + public boolean hasSingleSeasonSupport() { + return false; + } + + + @Override + public Collection getEpisodeList(SearchResult searchResult, int season) throws Exception { + throw new UnsupportedOperationException(); + } + + @Override public URI getEpisodeListLink(SearchResult searchResult, int season) { - return ((HyperLink) searchResult).toUri(); + throw new UnsupportedOperationException(); } @@ -129,9 +149,9 @@ public class AnidbClient extends EpisodeListClient { String qs = URLEncoder.encode(searchterm, "UTF-8"); // type=2 -> only TV Series - String file = "/perl-bin/animedb.pl?type=2&show=animelist&orderby.name=0.1&orderbar=0&noalias=1&do.search=Search&adb.search=" + qs; + String path = "/perl-bin/animedb.pl?type=2&show=animelist&orderby.name=0.1&orderbar=0&noalias=1&do.search=Search&adb.search=" + qs; - return new URL("http", host, file); + return new URL("http", host, path); } } diff --git a/source/net/sourceforge/filebot/web/EpisodeListClient.java b/source/net/sourceforge/filebot/web/EpisodeListClient.java index 2c21244a..b3d5dcb1 100644 --- a/source/net/sourceforge/filebot/web/EpisodeListClient.java +++ b/source/net/sourceforge/filebot/web/EpisodeListClient.java @@ -8,7 +8,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import javax.swing.ImageIcon; +import javax.swing.Icon; public abstract class EpisodeListClient { @@ -20,7 +20,7 @@ public abstract class EpisodeListClient { if (registry == null) { registry = new ArrayList(3); - registry.add(new TvdotcomClient()); + registry.add(new TVDotComClient()); registry.add(new AnidbClient()); registry.add(new TVRageClient()); } @@ -29,41 +29,43 @@ public abstract class EpisodeListClient { } private final String name; - private final boolean singleSeasonSupported; - private final ImageIcon icon; + private final Icon icon; - public EpisodeListClient(String name, ImageIcon icon, boolean singleSeasonSupported) { + public EpisodeListClient(String name, Icon icon) { this.name = name; this.icon = icon; - this.singleSeasonSupported = singleSeasonSupported; } public abstract Collection search(String searchterm) throws Exception; + public abstract boolean hasSingleSeasonSupport(); + + + public abstract Collection getEpisodeList(SearchResult searchResult) throws Exception; + + public abstract Collection getEpisodeList(SearchResult searchResult, int season) throws Exception; + public abstract URI getEpisodeListLink(SearchResult searchResult); + + public abstract URI getEpisodeListLink(SearchResult searchResult, int season); - public boolean isSingleSeasonSupported() { - return singleSeasonSupported; - } - - - public ImageIcon getIcon() { - return icon; - } - - public String getName() { return name; } + public Icon getIcon() { + return icon; + } + + @Override public String toString() { return name; diff --git a/source/net/sourceforge/filebot/web/HyperLink.java b/source/net/sourceforge/filebot/web/HyperLink.java index d145b593..88c7cfc2 100644 --- a/source/net/sourceforge/filebot/web/HyperLink.java +++ b/source/net/sourceforge/filebot/web/HyperLink.java @@ -3,27 +3,27 @@ package net.sourceforge.filebot.web; import java.net.URI; -import java.net.URL; +import java.net.URISyntaxException; public class HyperLink extends SearchResult { - private final URL url; + private final URI uri; - public HyperLink(String name, URL url) { + public HyperLink(String name, URI uri) { super(name); - this.url = url; + this.uri = uri; } - public URL getUrl() { - return url; + public HyperLink(String name, String uri) throws URISyntaxException { + this(name, new URI(uri)); } - public URI toUri() { - return URI.create(url.toString()); + public URI getURI() { + return uri; } } diff --git a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java index d726a95e..035f68e4 100644 --- a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; @@ -57,10 +58,11 @@ public class SubsceneSubtitleClient extends SubtitleClient { String href = XPathUtil.selectString("@href", node); try { - URL url = new URL("http", host, href); + //TODO which exception? + URI url = new URI("http", host, href); searchResults.add(new HyperLink(title, url)); - } catch (MalformedURLException e) { + } catch (URISyntaxException e) { Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid href: " + href, e); } } @@ -168,7 +170,7 @@ public class SubsceneSubtitleClient extends SubtitleClient { @Override public URI getSubtitleListLink(SearchResult searchResult) { - return ((HyperLink) searchResult).toUri(); + return ((HyperLink) searchResult).getURI(); } diff --git a/source/net/sourceforge/filebot/web/TVRageClient.java b/source/net/sourceforge/filebot/web/TVRageClient.java index d2e8c850..47319623 100644 --- a/source/net/sourceforge/filebot/web/TVRageClient.java +++ b/source/net/sourceforge/filebot/web/TVRageClient.java @@ -28,7 +28,13 @@ public class TVRageClient extends EpisodeListClient { public TVRageClient() { - super("TVRage", ResourceManager.getIcon("search.tvrage"), true); + super("TVRage", ResourceManager.getIcon("search.tvrage")); + } + + + @Override + public boolean hasSingleSeasonSupport() { + return true; } @@ -60,53 +66,48 @@ public class TVRageClient extends EpisodeListClient { } - @Override - public List getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException, ParserConfigurationException { - + private EpisodeListFeed getEpisodeListFeed(SearchResult searchResult) throws SAXException, IOException, ParserConfigurationException { int showId = ((TVRageSearchResult) searchResult).getShowId(); String episodeListUri = String.format("http://" + host + "/feeds/episode_list.php?sid=" + showId); Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(episodeListUri); - int numberOfSeasons = XPathUtil.selectInteger("Show/totalseasons", dom); - - if (season > numberOfSeasons) - throw new IllegalArgumentException(String.format("%s only has %d seasons", searchResult.getName(), numberOfSeasons)); - - Node episodeListNode = XPathUtil.selectNode("Show/Episodelist", dom); - - boolean allSeasons = (season == 0); - - List episodes = new ArrayList(24); - - for (int i = 0; i <= numberOfSeasons; i++) { - if (i == season || allSeasons) { - List nodes = XPathUtil.selectNodes("Season" + i + "/episode", episodeListNode); - - for (Node node : nodes) { - String title = XPathUtil.selectString("title", node); - String episodeNumber = XPathUtil.selectString("seasonnum", node); - String seasonNumber = Integer.toString(i); - - episodes.add(new Episode(searchResult.getName(), seasonNumber, episodeNumber, title)); - } - } - } - - return episodes; + return new EpisodeListFeed(dom); + } + + + @Override + public List getEpisodeList(SearchResult searchResult) throws Exception { + return getEpisodeListFeed(searchResult).getEpisodeList(); + } + + + @Override + public List getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException, ParserConfigurationException { + return getEpisodeListFeed(searchResult).getEpisodeList(season); + } + + + @Override + public URI getEpisodeListLink(SearchResult searchResult) { + return getEpisodeListLink(searchResult, "all"); } @Override public URI getEpisodeListLink(SearchResult searchResult, int season) { - String page = ((TVRageSearchResult) searchResult).getLink(); - String seasonString = (season >= 1) ? Integer.toString(season) : "all"; + return getEpisodeListLink(searchResult, Integer.toString(season)); + } + + + private URI getEpisodeListLink(SearchResult searchResult, String seasonString) { + String base = ((TVRageSearchResult) searchResult).getLink(); - return URI.create(page + "/episode_list/" + seasonString); + return URI.create(base + "/episode_list/" + seasonString); } - public static class TVRageSearchResult extends SearchResult { + protected static class TVRageSearchResult extends SearchResult { private final int showId; private final String link; @@ -130,4 +131,64 @@ public class TVRageClient extends EpisodeListClient { } + + private static class EpisodeListFeed { + + private final String name; + + private final int totalSeasons; + + private final Node episodeListNode; + + + public EpisodeListFeed(Document dom) { + name = XPathUtil.selectString("Show/name", dom); + totalSeasons = XPathUtil.selectInteger("Show/totalseasons", dom); + + episodeListNode = XPathUtil.selectNode("Show/Episodelist", dom); + } + + + public String getName() { + return name; + } + + + public int getTotalSeasons() { + return totalSeasons; + } + + + public List getEpisodeList() { + List episodes = new ArrayList(150); + + for (int i = 0; i <= getTotalSeasons(); i++) { + episodes.addAll(getEpisodeList(i)); + } + + return episodes; + } + + + public List getEpisodeList(int season) { + if (season > getTotalSeasons() || season < 0) + throw new IllegalArgumentException(String.format("%s only has %d seasons", getName(), getTotalSeasons())); + + String seasonString = Integer.toString(season); + List nodes = XPathUtil.selectNodes("Season" + seasonString + "/episode", episodeListNode); + + List episodes = new ArrayList(nodes.size()); + + for (Node node : nodes) { + String title = XPathUtil.selectString("title", node); + String episodeNumber = XPathUtil.selectString("seasonnum", node); + + episodes.add(new Episode(getName(), seasonString, episodeNumber, title)); + } + + return episodes; + } + + } + } diff --git a/test/net/sourceforge/filebot/web/TVDotComClientTest.java b/test/net/sourceforge/filebot/web/TVDotComClientTest.java new file mode 100644 index 00000000..187586eb --- /dev/null +++ b/test/net/sourceforge/filebot/web/TVDotComClientTest.java @@ -0,0 +1,97 @@ + +package net.sourceforge.filebot.web; + + +import static org.junit.Assert.assertEquals; + +import java.net.URI; +import java.util.List; + +import org.junit.Test; + + +public class TVDotComClientTest { + + private static TVDotComClient tvdotcom = new TVDotComClient(); + + private static HyperLink testResult = new HyperLink("Buffy the Vampire Slayer", URI.create("http://www.tv.com/buffy-the-vampire-slayer/show/10/episode_listings.html")); + private static HyperLink singleSeasonTestResult = new HyperLink("Firefly", URI.create("http://www.tv.com/firefly/show/7097/episode_listings.html")); + private static HyperLink manySeasonsTestResult = new HyperLink("Doctor Who", URI.create("http://www.tv.com/doctor-who/show/355/episode_listings.html")); + + + @Test + public void search() throws Exception { + List results = tvdotcom.search("Buffy"); + + HyperLink result = (HyperLink) results.get(0); + + assertEquals(testResult.getName(), result.getName()); + assertEquals(testResult.getURI(), result.getURI()); + } + + + @Test + public void getEpisodeList() throws Exception { + List results = tvdotcom.getEpisodeList(testResult, 7); + + assertEquals(22, results.size()); + + Episode chosen = results.get(21); + + assertEquals("Buffy the Vampire Slayer", chosen.getShowName()); + assertEquals("Chosen", chosen.getTitle()); + assertEquals("22", chosen.getNumberOfEpisode()); + assertEquals("7", chosen.getNumberOfSeason()); + } + + + @Test + public void getEpisodeListAllMultiSeason() throws Exception { + List list = tvdotcom.getEpisodeList(testResult); + + assertEquals(145, list.size()); + + Episode first = list.get(0); + + assertEquals("Buffy the Vampire Slayer", first.getShowName()); + assertEquals("Unaired Pilot", first.getTitle()); + assertEquals("Pilot", first.getNumberOfEpisode()); + assertEquals("1", first.getNumberOfSeason()); + } + + + @Test + public void getEpisodeListAllSingleSeason() throws Exception { + List list = tvdotcom.getEpisodeList(singleSeasonTestResult); + + assertEquals(15, list.size()); + + Episode fourth = list.get(3); + + assertEquals("Firefly", fourth.getShowName()); + assertEquals("Jaynestown", fourth.getTitle()); + assertEquals("04", fourth.getNumberOfEpisode()); + assertEquals("1", fourth.getNumberOfSeason()); + } + + + @Test + public void getEpisodeListAllManySeasons() throws Exception { + List list = tvdotcom.getEpisodeList(manySeasonsTestResult); + + assertEquals(708, list.size()); + } + + + @Test + public void getEpisodeListLink() { + assertEquals(tvdotcom.getEpisodeListLink(testResult, 1).toString(), "http://www.tv.com/buffy-the-vampire-slayer/show/10/episode_listings.html?season=1"); + } + + + @Test + public void getEpisodeListLinkAll() { + assertEquals(tvdotcom.getEpisodeListLink(testResult, 0).toString(), "http://www.tv.com/buffy-the-vampire-slayer/show/10/episode_listings.html?season=0"); + } + +} diff --git a/test/net/sourceforge/filebot/web/TVRageClientTest.java b/test/net/sourceforge/filebot/web/TVRageClientTest.java index 8ca84026..d9da8588 100644 --- a/test/net/sourceforge/filebot/web/TVRageClientTest.java +++ b/test/net/sourceforge/filebot/web/TVRageClientTest.java @@ -13,8 +13,8 @@ import org.junit.Test; public class TVRageClientTest { - private TVRageClient tvrage = new TVRageClient(); - private TVRageSearchResult testResult = new TVRageSearchResult("Buffy the Vampire Slayer", 2930, "http://www.tvrage.com/Buffy_The_Vampire_Slayer"); + private static TVRageClient tvrage = new TVRageClient(); + private static TVRageSearchResult testResult = new TVRageSearchResult("Buffy the Vampire Slayer", 2930, "http://www.tvrage.com/Buffy_The_Vampire_Slayer"); @Test @@ -33,6 +33,8 @@ public class TVRageClientTest { public void getEpisodeList() throws Exception { List list = tvrage.getEpisodeList(testResult, 7); + assertEquals(22, list.size()); + Episode chosen = list.get(21); assertEquals("Buffy the Vampire Slayer", chosen.getShowName()); @@ -44,7 +46,7 @@ public class TVRageClientTest { @Test public void getEpisodeListAll() throws Exception { - List list = tvrage.getEpisodeList(testResult, 0); + List list = tvrage.getEpisodeList(testResult); assertEquals(145, list.size()); @@ -65,8 +67,13 @@ public class TVRageClientTest { @Test public void getEpisodeListLink() throws Exception { - assertEquals(tvrage.getEpisodeListLink(testResult, 0).toString(), "http://www.tvrage.com/Buffy_The_Vampire_Slayer/episode_list/all"); assertEquals(tvrage.getEpisodeListLink(testResult, 1).toString(), "http://www.tvrage.com/Buffy_The_Vampire_Slayer/episode_list/1"); } + + @Test + public void getEpisodeListLinkAll() throws Exception { + assertEquals(tvrage.getEpisodeListLink(testResult).toString(), "http://www.tvrage.com/Buffy_The_Vampire_Slayer/episode_list/all"); + } + } diff --git a/test/net/sourceforge/filebot/web/WebTestSuite.java b/test/net/sourceforge/filebot/web/WebTestSuite.java index a051f77f..e5a89619 100644 --- a/test/net/sourceforge/filebot/web/WebTestSuite.java +++ b/test/net/sourceforge/filebot/web/WebTestSuite.java @@ -11,7 +11,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses( { TVRageClientTest.class }) +@SuiteClasses( { TVDotComClientTest.class, TVRageClientTest.class }) public class WebTestSuite { public static Test suite() {