* added episode list support for IMDb

This commit is contained in:
Reinhard Pointner 2009-03-17 21:59:19 +00:00
parent 90c8af354d
commit df143e0305
12 changed files with 257 additions and 18 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

View File

@ -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("(?<!\\p{Alnum})[Ss](\\d{1,2})[^\\p{Alnum}]{0,3}[Ee](\\d{1,3})(?!\\p{Digit})");
// match patterns like 1x01, 1.02, ... 10x01, 10.02, ...
// match patterns like 1x01, 1.02, ..., 1x01a, 10x01, 10.02, ...
patterns[1] = new SeasonEpisodePattern("(?<!\\p{Alnum})(\\d{1,2})[x\\.](\\d{1,3})(?!\\p{Digit})");
// match patterns like 01, 102, 1003 (enclosed in separators)

View File

@ -30,6 +30,7 @@ import net.sourceforge.filebot.ui.transfer.SaveAction;
import net.sourceforge.filebot.web.AnidbClient;
import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.EpisodeListClient;
import net.sourceforge.filebot.web.IMDbClient;
import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.filebot.web.TVDotComClient;
import net.sourceforge.filebot.web.TVRageClient;
@ -74,6 +75,7 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListClient, Epi
engines.add(new TVRageClient());
engines.add(new AnidbClient());
engines.add(new TVDotComClient());
engines.add(new IMDbClient());
engines.add(new TheTVDBClient(Settings.userRoot().get("thetvdb.apikey")));
return engines;
@ -220,10 +222,11 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListClient, Epi
@Override
public URI getLink() {
if (request.getSeason() != ALL_SEASONS)
if (request.getSeason() != ALL_SEASONS) {
return request.getClient().getEpisodeListLink(getSearchResult(), request.getSeason());
else
return request.getClient().getEpisodeListLink(getSearchResult());
}
return request.getClient().getEpisodeListLink(getSearchResult());
}

View File

@ -79,7 +79,7 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer {
String extension = FileUtilities.getExtension(file);
if (!extension.isEmpty())
if (extension != null)
return extension;
// some file with no extension

View File

@ -42,6 +42,7 @@ import net.sourceforge.filebot.ui.SelectDialog;
import net.sourceforge.filebot.web.AnidbClient;
import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.EpisodeListClient;
import net.sourceforge.filebot.web.IMDbClient;
import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.filebot.web.TVDotComClient;
import net.sourceforge.filebot.web.TVRageClient;
@ -143,6 +144,7 @@ public class RenamePanel extends JComponent {
actionPopup.add(new AutoFetchEpisodeListAction(new TVRageClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new AnidbClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new TVDotComClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new IMDbClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new TheTVDBClient(Settings.userRoot().get("thetvdb.apikey"))));
actionPopup.addSeparator();

View File

@ -141,7 +141,7 @@ public class AnidbClient implements EpisodeListClient {
@Override
public URI getEpisodeListLink(SearchResult searchResult, int season) {
throw new UnsupportedOperationException();
return null;
}
}

View File

@ -0,0 +1,162 @@
package net.sourceforge.filebot.web;
import static net.sourceforge.filebot.web.WebRequest.getHtmlDocument;
import static net.sourceforge.tuned.XPathUtilities.getAttribute;
import static net.sourceforge.tuned.XPathUtilities.selectNodes;
import static net.sourceforge.tuned.XPathUtilities.selectString;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import net.sourceforge.filebot.ResourceManager;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
public class IMDbClient implements EpisodeListClient {
private static final String host = "www.imdb.com";
@Override
public String getName() {
return "IMDb";
}
@Override
public Icon getIcon() {
return ResourceManager.getIcon("search.imdb");
}
@Override
public boolean hasSingleSeasonSupport() {
return true;
}
@Override
public List<SearchResult> 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<Node> nodes = selectNodes("//TABLE//A[following-sibling::SMALL[contains(.,'TV series')]]", dom);
List<SearchResult> results = new ArrayList<SearchResult>(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<Episode> getEpisodeList(SearchResult searchResult) throws IOException, SAXException {
Document dom = getHtmlDocument(openConnection(getEpisodeListLink(searchResult).toURL()));
String seriesName = removeQuotationMarks(selectString("//H1/A", dom));
List<Node> nodes = selectNodes("//TABLE//H3/A[preceding-sibling::text()]", dom);
List<Episode> episodes = new ArrayList<Episode>(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<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception {
List<Episode> episodes = new ArrayList<Episode>(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;
}
}

View File

@ -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));
}

View File

@ -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;

View File

@ -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<SearchResult> 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<Episode> 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<Episode> 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\"\""));
}
}

View File

@ -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 {
}