* switched back to using List as return value for EpisodeList- and SubtitleClients (lazy XPath evaluation not needed anymore, because we are fast enough anyway)

This commit is contained in:
Reinhard Pointner 2008-07-05 11:37:03 +00:00
parent d1775cf1b4
commit ea6a839aa8
22 changed files with 209 additions and 418 deletions

View File

@ -8,6 +8,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -30,7 +31,6 @@ import javax.swing.SwingWorker;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.tuned.ExceptionUtil;
import net.sourceforge.tuned.ProgressIterator;
import net.sourceforge.tuned.ui.SelectButtonTextField;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
import net.sourceforge.tuned.ui.TunedUtil;
@ -137,7 +137,7 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
};
protected abstract class SearchTask extends SwingWorker<List<SearchResult>, Void> {
protected abstract class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
private final String searchText;
private final S client;
@ -153,7 +153,7 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
@Override
protected abstract List<SearchResult> doInBackground() throws Exception;
protected abstract Collection<SearchResult> doInBackground() throws Exception;
public String getSearchText() {
@ -240,13 +240,13 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
private SearchResult selectSearchResult(SearchTask task) throws Exception {
List<SearchResult> searchResults = task.get();
Collection<SearchResult> searchResults = task.get();
switch (searchResults.size()) {
case 0:
return null;
case 1:
return searchResults.get(0);
return searchResults.iterator().next();
}
// multiple results have been found, user must selected one
@ -288,17 +288,10 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
protected final Void doInBackground() throws Exception {
long start = System.currentTimeMillis();
ProgressIterator<E> iterator = fetch();
Collection<E> results = fetch();
while (!isCancelled() && iterator.hasNext()) {
try {
publish(iterator.next());
} catch (Exception e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.getMessage());
}
setProgress((iterator.getPosition() * 100) / iterator.getLength());
for (E result : results) {
publish(result);
count++;
}
@ -308,7 +301,7 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
}
protected abstract ProgressIterator<E> fetch() throws Exception;
protected abstract Collection<E> fetch() throws Exception;
@Override

View File

@ -33,7 +33,7 @@ class FetchEpisodeListTask extends SwingWorker<List<Episode>, Void> {
protected List<Episode> doInBackground() throws Exception {
long start = System.currentTimeMillis();
Iterator<Episode> itr = searchEngine.getEpisodeList(searchResult, numberOfSeason);
Iterator<Episode> itr = searchEngine.getEpisodeList(searchResult, numberOfSeason).iterator();
ArrayList<Episode> list = new ArrayList<Episode>();

View File

@ -11,7 +11,6 @@ import java.beans.PropertyChangeListener;
import java.net.URI;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -185,7 +184,7 @@ public class SearchPanel extends FileBotPanel {
};
private class SearchTask extends SwingWorker<List<SearchResult>, Void> {
private class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
private final String query;
private final EpisodeListClient client;
@ -200,7 +199,7 @@ public class SearchPanel extends FileBotPanel {
@Override
protected List<SearchResult> doInBackground() throws Exception {
protected Collection<SearchResult> doInBackground() throws Exception {
return client.search(query);
}
@ -242,7 +241,7 @@ public class SearchPanel extends FileBotPanel {
SearchTask task = (SearchTask) evt.getSource();
List<SearchResult> searchResults;
Collection<SearchResult> searchResults;
try {
searchResults = task.get();
@ -268,7 +267,7 @@ public class SearchPanel extends FileBotPanel {
if (searchResults.size() == 1) {
// only one show found, select this one
selectedResult = searchResults.get(0);
selectedResult = searchResults.iterator().next();
} else if (searchResults.size() > 1) {
// multiple shows found, let user selected one
Window window = SwingUtilities.getWindowAncestor(SearchPanel.this);

View File

@ -16,7 +16,7 @@ public class LanguageResolver {
return defaultInstance;
}
private final Map<String, Locale> localeMap = new HashMap<String, Locale>();
private final Map<String, Locale> cache = new HashMap<String, Locale>();
/**
@ -28,11 +28,11 @@ public class LanguageResolver {
public synchronized Locale getLocale(String languageName) {
languageName = languageName.toLowerCase();
Locale locale = localeMap.get(languageName);
Locale locale = cache.get(languageName);
if (locale == null) {
locale = findLocale(languageName);
localeMap.put(languageName, locale);
cache.put(languageName, locale);
}
return locale;

View File

@ -3,7 +3,10 @@ package net.sourceforge.filebot.ui.panel.subtitle;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import net.sourceforge.filebot.ListChangeSynchronizer;
import net.sourceforge.filebot.Settings;
@ -13,9 +16,6 @@ import net.sourceforge.filebot.ui.SelectDialog;
import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.filebot.web.SubtitleClient;
import net.sourceforge.filebot.web.SubtitleDescriptor;
import net.sourceforge.tuned.FunctionIterator;
import net.sourceforge.tuned.ProgressIterator;
import net.sourceforge.tuned.FunctionIterator.Function;
import net.sourceforge.tuned.ui.SimpleIconProvider;
@ -72,7 +72,7 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
@Override
protected List<SearchResult> doInBackground() throws Exception {
protected Collection<SearchResult> doInBackground() throws Exception {
return getClient().search(getSearchText());
}
@ -87,10 +87,16 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
@Override
protected ProgressIterator<SubtitlePackage> fetch() throws Exception {
ProgressIterator<SubtitleDescriptor> descriptors = getClient().getSubtitleList(getSearchResult());
protected Collection<SubtitlePackage> fetch() throws Exception {
//TODO language combobox
Collection<SubtitleDescriptor> descriptors = getClient().getSubtitleList(getSearchResult(), Locale.ENGLISH);
ArrayList<SubtitlePackage> packages = new ArrayList<SubtitlePackage>();
return new FunctionIterator<SubtitleDescriptor, SubtitlePackage>(descriptors, new SubtitlePackageFunction());
for (SubtitleDescriptor descriptor : descriptors) {
packages.add(new SubtitlePackage(descriptor));
}
return packages;
}
@ -109,14 +115,4 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
}
}
private static class SubtitlePackageFunction implements Function<SubtitleDescriptor, SubtitlePackage> {
@Override
public SubtitlePackage evaluate(SubtitleDescriptor sourceValue) {
return new SubtitlePackage(sourceValue);
}
}
}

View File

@ -17,10 +17,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.tuned.FunctionIterator;
import net.sourceforge.tuned.ProgressIterator;
import net.sourceforge.tuned.XPathUtil;
import net.sourceforge.tuned.FunctionIterator.Function;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@ -88,33 +85,19 @@ public class AnidbClient extends EpisodeListClient {
@Override
public ProgressIterator<Episode> getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException {
public List<Episode> getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException {
Document dom = HtmlUtil.getHtmlDocument(getEpisodeListLink(searchResult, season));
List<Node> nodes = XPathUtil.selectNodes("id('eplist')//TR/TD/SPAN/ancestor::TR", dom);
return new FunctionIterator<Node, Episode>(nodes, new EpisodeFunction(searchResult, nodes.size()));
}
private static class EpisodeFunction implements Function<Node, Episode> {
NumberFormat numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
numberFormat.setMinimumIntegerDigits(Math.max(Integer.toString(nodes.size()).length(), 2));
numberFormat.setGroupingUsed(false);
private final SearchResult searchResult;
private final NumberFormat numberFormat;
ArrayList<Episode> episodes = new ArrayList<Episode>(nodes.size());
public EpisodeFunction(SearchResult searchResult, int nodeCount) {
this.searchResult = searchResult;
numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
numberFormat.setMinimumIntegerDigits(Math.max(Integer.toString(nodeCount).length(), 2));
numberFormat.setGroupingUsed(false);
}
@Override
public Episode evaluate(Node node) {
for (Node node : nodes) {
String number = XPathUtil.selectString("./TD[contains(@class,'id')]/A", node);
String title = XPathUtil.selectString("./TD[@class='title']/LABEL/text()", node);
@ -129,15 +112,16 @@ public class AnidbClient extends EpisodeListClient {
}
// no seasons for anime
return new Episode(searchResult.getName(), null, number, title);
episodes.add(new Episode(searchResult.getName(), null, number, title));
}
return episodes;
}
@Override
public URI getEpisodeListLink(SearchResult searchResult, int season) {
return ((HyperLink) searchResult).getUri();
return ((HyperLink) searchResult).toUri();
}

View File

@ -4,13 +4,12 @@ package net.sourceforge.filebot.web;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.swing.ImageIcon;
import net.sourceforge.tuned.ProgressIterator;
public abstract class EpisodeListClient {
@ -41,10 +40,10 @@ public abstract class EpisodeListClient {
}
public abstract List<SearchResult> search(String searchterm) throws Exception;
public abstract Collection<SearchResult> search(String searchterm) throws Exception;
public abstract ProgressIterator<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception;
public abstract Collection<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception;
public abstract URI getEpisodeListLink(SearchResult searchResult, int season);

View File

@ -52,8 +52,11 @@ public class HtmlUtil {
public static Document getHtmlDocument(URL url) throws IOException, SAXException {
URLConnection connection = url.openConnection();
return getHtmlDocument(url.openConnection());
}
public static Document getHtmlDocument(URLConnection connection) throws IOException, SAXException {
Charset charset = getCharset(connection.getContentType());
String encoding = connection.getContentEncoding();
InputStream inputStream = connection.getInputStream();

View File

@ -8,23 +8,22 @@ import java.net.URL;
public class HyperLink extends SearchResult {
private final URI uri;
private final URL url;
public HyperLink(String name, URI uri) {
super(name);
this.uri = uri;
}
public HyperLink(String name, URL url) {
super(name);
this.uri = URI.create(url.toString());
this.url = url;
}
public URI getUri() {
return uri;
public URL getUrl() {
return url;
}
public URI toUri() {
return URI.create(url.toString());
}
}

View File

@ -4,8 +4,10 @@ package net.sourceforge.filebot.web;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -155,20 +157,18 @@ public class OpenSubtitlesClient {
@SuppressWarnings("unchecked")
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int... imdbidArray) throws XmlRpcFault {
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int imdbid, Locale language) throws XmlRpcFault {
List<Map<String, String>> imdbidList = new ArrayList<Map<String, String>>(imdbidArray.length);
Map<String, String> searchListEntry = new HashMap<String, String>(2);
for (int imdbid : imdbidArray) {
Map<String, String> map = new HashMap<String, String>(1);
// pad id with zeros
map.put("imdbid", String.format("%07d", imdbid));
imdbidList.add(map);
}
// pad imdbId with zeros
//TODO needed???
searchListEntry.put("imdbid", String.format("%07d", imdbid));
searchListEntry.put("sublanguageid", getSubLanguageID(language));
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, imdbidList);
List<Map<String, String>> searchList = Collections.singletonList(searchListEntry);
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, searchList);
ArrayList<OpenSubtitlesSubtitleDescriptor> subs = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
@ -184,6 +184,12 @@ public class OpenSubtitlesClient {
}
private String getSubLanguageID(Locale locale) {
//TODO test if sublanguageid is really ISO3 language code
return locale.getISO3Language();
}
@SuppressWarnings("unchecked")
public List<MovieDescriptor> searchMoviesOnIMDB(String query) throws XmlRpcFault {

View File

@ -3,7 +3,9 @@ package net.sourceforge.filebot.web;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
@ -11,9 +13,6 @@ import java.util.logging.Logger;
import net.sourceforge.filebot.Settings;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.tuned.FunctionIterator;
import net.sourceforge.tuned.ProgressIterator;
import net.sourceforge.tuned.FunctionIterator.Function;
/**
@ -44,15 +43,14 @@ public class OpenSubtitlesSubtitleClient extends SubtitleClient {
}
@SuppressWarnings("unchecked")
@Override
public ProgressIterator<SubtitleDescriptor> getSubtitleList(SearchResult searchResult) throws Exception {
public Collection<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, Locale language) throws Exception {
login();
int imdbId = ((MovieDescriptor) searchResult).getImdbId();
List<OpenSubtitlesSubtitleDescriptor> subtitles = client.searchSubtitles(imdbId);
return new FunctionIterator<OpenSubtitlesSubtitleDescriptor, SubtitleDescriptor>(subtitles, new SubtitleFunction());
return (Collection) client.searchSubtitles(imdbId, language);
}
@ -135,13 +133,4 @@ public class OpenSubtitlesSubtitleClient extends SubtitleClient {
};
}
private static class SubtitleFunction implements Function<OpenSubtitlesSubtitleDescriptor, SubtitleDescriptor> {
@Override
public SubtitleDescriptor evaluate(OpenSubtitlesSubtitleDescriptor sourceValue) {
return sourceValue;
}
}
}

View File

@ -2,28 +2,26 @@
package net.sourceforge.filebot.web;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
public class SearchResultCache {
private final Map<String, SearchResult> cache = Collections.synchronizedMap(new TreeMap<String, SearchResult>(String.CASE_INSENSITIVE_ORDER));
private final ConcurrentHashMap<String, SearchResult> cache = new ConcurrentHashMap<String, SearchResult>();
public boolean containsKey(String name) {
return cache.containsKey(name);
return cache.containsKey(key(name));
}
public SearchResult get(String name) {
return cache.get(name);
return cache.get(key(name));
}
public void add(SearchResult searchResult) {
cache.put(searchResult.getName(), searchResult);
cache.putIfAbsent(key(searchResult.getName()), searchResult);
}
@ -32,4 +30,10 @@ public class SearchResultCache {
add(searchResult);
}
}
private String key(String name) {
return name.toLowerCase();
}
}

View File

@ -7,10 +7,13 @@ import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@ -18,10 +21,7 @@ import java.util.regex.Pattern;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.tuned.FileUtil;
import net.sourceforge.tuned.FunctionIterator;
import net.sourceforge.tuned.ProgressIterator;
import net.sourceforge.tuned.XPathUtil;
import net.sourceforge.tuned.FunctionIterator.Function;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@ -70,69 +70,105 @@ public class SubsceneSubtitleClient extends SubtitleClient {
return searchResults;
}
HashMap<String, String> languageIdCache;
public String getLanguageID(Locale language) {
return languageIdCache.get(language.getDisplayLanguage(Locale.ENGLISH).toLowerCase());
}
@Override
public ProgressIterator<SubtitleDescriptor> getSubtitleList(SearchResult searchResult) throws Exception {
public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, Locale language) throws Exception {
URL url = getSubtitleListLink(searchResult).toURL();
Document dom = HtmlUtil.getHtmlDocument(url);
Document dom = null;
if (languageIdCache != null) {
URLConnection connection = url.openConnection();
if (language != null && language != Locale.ROOT) {
System.out.println(getLanguageID(language));
connection.addRequestProperty("Cookie", "subscene_sLanguageIds=" + getLanguageID(language));
}
dom = HtmlUtil.getHtmlDocument(connection);
} else {
URLConnection connection = url.openConnection();
dom = HtmlUtil.getHtmlDocument(connection);
List<Node> nodes = XPathUtil.selectNodes("//DIV[@class='languageList']/DIV", dom);
Pattern onClickPattern = Pattern.compile("selectLanguage\\((\\d+)\\);");
languageIdCache = new HashMap<String, String>();
for (Node node : nodes) {
Matcher matcher = onClickPattern.matcher(XPathUtil.selectString("./INPUT/@onclick", node));
if (matcher.matches()) {
String name = XPathUtil.selectString("./LABEL/text()", node);
String id = matcher.group(1);
//TODO sysout
System.out.println(name + " = " + id);
languageIdCache.put(name.toLowerCase(), id);
}
}
}
List<Node> nodes = XPathUtil.selectNodes("//TABLE[@class='filmSubtitleList']//A[@id]//ancestor::TR", dom);
return new FunctionIterator<Node, SubtitleDescriptor>(nodes, new SubtitleFunction(url));
Pattern hrefPattern = Pattern.compile("javascript:Subtitle\\((\\d+), '(\\w+)', '\\d+', '(\\d+)'\\);");
ArrayList<SubtitleDescriptor> subtitles = new ArrayList<SubtitleDescriptor>(nodes.size());
for (Node node : nodes) {
try {
Node linkNode = XPathUtil.selectFirstNode("./TD[1]/A", node);
String lang = XPathUtil.selectString("./SPAN[1]", linkNode);
String href = XPathUtil.selectString("@href", linkNode);
String name = XPathUtil.selectString("./SPAN[2]", linkNode);
String author = XPathUtil.selectString("./TD[4]", node);
Matcher matcher = hrefPattern.matcher(href);
if (!matcher.matches())
throw new IllegalArgumentException("Cannot extract download parameters: " + href);
String subtitleId = matcher.group(1);
String typeId = matcher.group(2);
URL downloadUrl = getDownloadUrl(url, subtitleId, typeId);
subtitles.add(new SubsceneSubtitleDescriptor(name, lang, author, typeId, downloadUrl, url));
} catch (Exception e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Cannot parse subtitle node", e);
}
}
return subtitles;
}
private static class SubtitleFunction implements Function<Node, SubtitleDescriptor> {
private final Pattern hrefPattern = Pattern.compile("javascript:Subtitle\\((\\d+), '(\\w+)', '0', '(\\d+)'\\);");
private final URL url;
public SubtitleFunction(URL url) {
this.url = url;
}
@Override
public SubtitleDescriptor evaluate(Node node) throws Exception {
Node linkNode = XPathUtil.selectFirstNode("./TD[1]/A", node);
String href = XPathUtil.selectString("@href", linkNode);
String lang = XPathUtil.selectString("./SPAN[1]", linkNode);
String name = XPathUtil.selectString("./SPAN[2]", linkNode);
String author = XPathUtil.selectString("./TD[4]", node);
Matcher matcher = hrefPattern.matcher(href);
if (!matcher.matches())
throw new IllegalArgumentException("Cannot extract download parameters: " + href);
String subtitleId = matcher.group(1);
String typeId = matcher.group(2);
URL downloadUrl = getDownloadUrl(url, subtitleId, typeId);
return new SubsceneSubtitleDescriptor(name, lang, author, typeId, downloadUrl, url);
}
private URL getDownloadUrl(URL referer, String subtitleId, String typeId) throws MalformedURLException {
String basePath = FileUtil.getNameWithoutExtension(referer.getFile());
String path = String.format("%s-dlpath-%s/%s.zipx", basePath, subtitleId, typeId);
return new URL(referer.getProtocol(), referer.getHost(), path);
}
private URL getDownloadUrl(URL referer, String subtitleId, String typeId) throws MalformedURLException {
String basePath = FileUtil.getNameWithoutExtension(referer.getFile());
String path = String.format("%s-dlpath-%s/%s.zipx", basePath, subtitleId, typeId);
return new URL(referer.getProtocol(), referer.getHost(), path);
}
@Override
public URI getSubtitleListLink(SearchResult searchResult) {
return ((HyperLink) searchResult).getUri();
return ((HyperLink) searchResult).toUri();
}

View File

@ -4,12 +4,12 @@ package net.sourceforge.filebot.web;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.swing.ImageIcon;
import net.sourceforge.tuned.ProgressIterator;
import javax.swing.Icon;
public abstract class SubtitleClient {
@ -28,20 +28,20 @@ public abstract class SubtitleClient {
return Collections.unmodifiableList(registry);
}
private String name;
private ImageIcon icon;
private final String name;
private final Icon icon;
public SubtitleClient(String name, ImageIcon icon) {
public SubtitleClient(String name, Icon icon) {
this.name = name;
this.icon = icon;
}
public abstract List<SearchResult> search(String searchterm) throws Exception;
public abstract Collection<SearchResult> search(String searchterm) throws Exception;
public abstract ProgressIterator<SubtitleDescriptor> getSubtitleList(SearchResult searchResult) throws Exception;
public abstract Collection<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, Locale language) throws Exception;
public abstract URI getSubtitleListLink(SearchResult searchResult);
@ -52,7 +52,7 @@ public abstract class SubtitleClient {
}
public ImageIcon getIcon() {
public Icon getIcon() {
return icon;
}

View File

@ -13,8 +13,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.tuned.DefaultProgressIterator;
import net.sourceforge.tuned.ProgressIterator;
import net.sourceforge.tuned.XPathUtil;
import org.w3c.dom.Document;
@ -63,7 +61,7 @@ public class TVRageClient extends EpisodeListClient {
@Override
public ProgressIterator<Episode> getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException, ParserConfigurationException {
public List<Episode> getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException, ParserConfigurationException {
int showId = ((TVRageSearchResult) searchResult).getShowId();
String episodeListUri = String.format("http://" + host + "/feeds/episode_list.php?sid=" + showId);
@ -95,7 +93,7 @@ public class TVRageClient extends EpisodeListClient {
}
}
return new DefaultProgressIterator<Episode>(episodes);
return episodes;
}

View File

@ -18,10 +18,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.tuned.FunctionIterator;
import net.sourceforge.tuned.ProgressIterator;
import net.sourceforge.tuned.XPathUtil;
import net.sourceforge.tuned.FunctionIterator.Function;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@ -77,39 +74,22 @@ public class TvdotcomClient extends EpisodeListClient {
@Override
public ProgressIterator<Episode> getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException {
public List<Episode> getEpisodeList(SearchResult searchResult, int season) throws IOException, SAXException {
Document dom = HtmlUtil.getHtmlDocument(getEpisodeListLink(searchResult, season));
List<Node> nodes = XPathUtil.selectNodes("id('episode-listing')/DIV/TABLE/TR/TD/ancestor::TR", dom);
return new FunctionIterator<Node, Episode>(nodes, new EpisodeFunction(searchResult, season, nodes.size()));
}
private static class EpisodeFunction implements Function<Node, Episode> {
NumberFormat numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
numberFormat.setMinimumIntegerDigits(Math.max(Integer.toString(nodes.size()).length(), 2));
numberFormat.setGroupingUsed(false);
private final SearchResult searchResult;
private final NumberFormat numberFormat;
Integer episodeOffset = null;
String seasonString = (season >= 1) ? Integer.toString(season) : null;
private Integer episodeOffset = null;
private String seasonString = null;
ArrayList<Episode> episodes = new ArrayList<Episode>(nodes.size());
public EpisodeFunction(SearchResult searchResult, int season, int nodeCount) {
this.searchResult = searchResult;
numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
numberFormat.setMinimumIntegerDigits(Math.max(Integer.toString(nodeCount).length(), 2));
numberFormat.setGroupingUsed(false);
if (season >= 1)
seasonString = Integer.toString(season);
}
@Override
public Episode evaluate(Node node) {
for (Node node : nodes) {
String episodeNumber = XPathUtil.selectString("./TD[1]", node);
String title = XPathUtil.selectString("./TD[2]/A", node);
@ -125,16 +105,17 @@ public class TvdotcomClient extends EpisodeListClient {
// episode number may be "Pilot", "Special", ...
}
return new Episode(searchResult.getName(), seasonString, episodeNumber, title);
episodes.add(new Episode(searchResult.getName(), seasonString, episodeNumber, title));
}
return episodes;
}
@Override
public URI getEpisodeListLink(SearchResult searchResult, int season) {
String summaryFile = null;
summaryFile = ((HyperLink) searchResult).getUri().getPath();
String summaryFile = ((HyperLink) searchResult).getUrl().getPath();
String base = summaryFile.substring(0, summaryFile.indexOf("summary.html"));
String file = base + "episode_listings.html";

View File

@ -1,54 +0,0 @@
package net.sourceforge.tuned;
import java.util.Collection;
import java.util.Iterator;
public class DefaultProgressIterator<E> implements ProgressIterator<E> {
private final Iterator<E> sourceIterator;
private final int length;
private int position = 0;
public DefaultProgressIterator(Collection<E> source) {
this.sourceIterator = source.iterator();
this.length = source.size();
}
@Override
public int getLength() {
return length;
}
@Override
public int getPosition() {
return position;
}
@Override
public boolean hasNext() {
return sourceIterator.hasNext();
}
@Override
public E next() {
position++;
return sourceIterator.next();
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
}

View File

@ -15,6 +15,11 @@ public class DefaultThreadFactory implements ThreadFactory {
private final boolean daemon;
public DefaultThreadFactory(String name) {
this(name, Thread.NORM_PRIORITY);
}
public DefaultThreadFactory(String name, int priority) {
this(name, priority, false);
}

View File

@ -1,131 +0,0 @@
package net.sourceforge.tuned;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class FunctionIterator<S, T> implements ProgressIterator<T> {
/**
* A Function transforms one Object into another.
*
* @param <S> type of source Objects
* @param <T> type Objects are transformed into
*/
public static interface Function<S, T> {
/**
* Transform the given sourceValue into any kind of Object.
*
* @param sourceValue - the Object to transform
* @return the transformed version of the object
*/
public T evaluate(S sourceValue) throws Exception;
}
private final Iterator<S> sourceIterator;
private final Function<S, T> function;
private final int length;
private int position = 0;
public FunctionIterator(Collection<S> source, Function<S, T> function) {
this(source.iterator(), source.size(), function);
}
//TODO TEST case!!! for piped functions -> correct progress
public FunctionIterator(ProgressIterator<S> iterator, Function<S, T> function) {
this(iterator, iterator.getLength(), function);
}
public FunctionIterator(Iterator<S> iterator, int length, Function<S, T> function) {
this.sourceIterator = iterator;
this.length = length;
this.function = function;
}
@Override
public boolean hasNext() {
try {
return peekNext() != null;
} catch (Exception e) {
return true;
}
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
try {
return peekNext();
} finally {
cache = null;
currentException = null;
}
}
private T cache = null;
private RuntimeException currentException = null;
private T peekNext() {
while (cache == null && (sourceIterator.hasNext() || currentException != null)) {
if (currentException != null)
throw currentException;
try {
cache = transform(sourceIterator.next());
} catch (Exception e) {
currentException = ExceptionUtil.asRuntimeException(e);
}
position++;
}
return cache;
}
private T transform(S sourceValue) throws Exception {
return function.evaluate(sourceValue);
}
@Override
public int getPosition() {
if (sourceIterator instanceof FunctionIterator) {
return ((ProgressIterator<?>) sourceIterator).getPosition();
}
return position;
}
@Override
public int getLength() {
return length;
}
/**
* The remove operation is not supported by this implementation of <code>Iterator</code>.
*
* @throws UnsupportedOperationException if this method is invoked.
* @see java.util.Iterator
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@ -1,15 +0,0 @@
package net.sourceforge.tuned;
import java.util.Iterator;
public interface ProgressIterator<E> extends Iterator<E> {
public int getPosition();
public int getLength();
}

View File

@ -7,7 +7,6 @@ import static org.junit.Assert.assertEquals;
import java.util.List;
import net.sourceforge.filebot.web.TVRageClient.TVRageSearchResult;
import net.sourceforge.tuned.TestUtil;
import org.junit.Test;
@ -32,7 +31,7 @@ public class TVRageClientTest {
@Test
public void getEpisodeList() throws Exception {
List<Episode> list = TestUtil.asList(tvrage.getEpisodeList(testResult, 7));
List<Episode> list = tvrage.getEpisodeList(testResult, 7);
Episode chosen = list.get(21);
@ -45,7 +44,7 @@ public class TVRageClientTest {
@Test
public void getEpisodeListAll() throws Exception {
List<Episode> list = TestUtil.asList(tvrage.getEpisodeList(testResult, 0));
List<Episode> list = tvrage.getEpisodeList(testResult, 0);
assertEquals(145, list.size());

View File

@ -11,7 +11,7 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses( { FunctionIteratorTest.class, PreferencesMapTest.class, PreferencesListTest.class })
@SuiteClasses( { PreferencesMapTest.class, PreferencesListTest.class })
public class TunedTestSuite {
public static Test suite() {