* episode list caches are now case insensitive

* some refactoring
This commit is contained in:
Reinhard Pointner 2008-03-29 12:20:01 +00:00
parent 02057b3056
commit aed54eb060
9 changed files with 151 additions and 72 deletions

View File

@ -10,7 +10,7 @@ import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.EpisodeListClient;
class FetchEpisodesTask extends SwingWorker<List<Episode>, Object> {
class FetchEpisodeListTask extends SwingWorker<List<Episode>, Object> {
private String showName;
private EpisodeListClient searchEngine;
@ -18,7 +18,7 @@ class FetchEpisodesTask extends SwingWorker<List<Episode>, Object> {
private long duration;
public FetchEpisodesTask(EpisodeListClient searchEngine, String showname, int numberOfSeason) {
public FetchEpisodeListTask(EpisodeListClient searchEngine, String showname, int numberOfSeason) {
showName = showname;
this.searchEngine = searchEngine;
this.numberOfSeason = numberOfSeason;

View File

@ -25,6 +25,7 @@ import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
@ -76,7 +77,7 @@ public class SearchPanel extends FileBotPanel {
searchField.getTextField().setColumns(25);
searchFieldCompletion = new TextCompletion(searchField.getTextField());
searchFieldCompletion.addCompletionTerms(Settings.getSettings().getStringList(Settings.SEARCH_HISTORY));
searchFieldCompletion.addTerms(Settings.getSettings().getStringList(Settings.SEARCH_HISTORY));
searchFieldCompletion.hook();
JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
@ -111,6 +112,8 @@ public class SearchPanel extends FileBotPanel {
JScrollPane sp = new JScrollPane(history);
sp.setBorder(BorderFactory.createEmptyBorder());
sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
tabbedPane.addTab("History", ResourceManager.getIcon("tab.history"), sp);
mainPanel.add(searchBox, BorderLayout.NORTH);
@ -134,9 +137,9 @@ public class SearchPanel extends FileBotPanel {
private final PropertyChangeListener searchFieldListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
EpisodeListClient se = searchField.getSelectedValue();
EpisodeListClient client = searchField.getSelectedValue();
if (!se.isSingleSeasonSupported()) {
if (!client.isSingleSeasonSupported()) {
seasonSpinnerModel.setMaximum(SeasonSpinnerEditor.ALL_SEASONS);
seasonSpinnerModel.setValue(SeasonSpinnerEditor.ALL_SEASONS);
} else {
@ -277,8 +280,9 @@ public class SearchPanel extends FileBotPanel {
String title = task.getSearchTerm();
if (task.getNumberOfSeason() != SeasonSpinnerEditor.ALL_SEASONS)
title += " - Season " + task.getNumberOfSeason();
if (task.getNumberOfSeason() != SeasonSpinnerEditor.ALL_SEASONS) {
title += String.format(" - Season %d", task.getNumberOfSeason());
}
episodeList.setTitle(title);
episodeList.setIcon(task.getSearchEngine().getIcon());
@ -306,17 +310,23 @@ public class SearchPanel extends FileBotPanel {
tabbedPane.remove(episodeList);
Throwable cause = FileBotUtil.getRootCause(e);
MessageManager.showWarning(cause.getMessage());
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, cause.toString());
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, cause.toString());
return;
}
String showname = null;
if (shows.size() == 1)
if (task.getSearchEngine().getFoundName(task.getSearchTerm()) != null) {
// a show matching the search term exactly has already been found
showname = task.getSearchEngine().getFoundName(task.getSearchTerm());
} else if (shows.size() == 1) {
// only one show found, select this one
showname = shows.get(0);
else if (shows.size() > 1) {
} else if (shows.size() > 1) {
// multiple shows found, let user selected one
Window window = SwingUtilities.getWindowAncestor(SearchPanel.this);
SelectDialog<String> select = new SelectDialog<String>(window, shows);
@ -324,6 +334,7 @@ public class SearchPanel extends FileBotPanel {
select.setText("Select a Show:");
select.setIconImage(episodeList.getIcon().getImage());
select.setVisible(true);
showname = select.getSelectedValue();
} else {
MessageManager.showWarning("\"" + task.getSearchTerm() + "\" has not been found.");
@ -334,29 +345,31 @@ public class SearchPanel extends FileBotPanel {
return;
}
searchFieldCompletion.addCompletionTerm(showname);
Settings.getSettings().putStringList(Settings.SEARCH_HISTORY, searchFieldCompletion.getCompletionTerms());
searchFieldCompletion.addTerm(showname);
Settings.getSettings().putStringList(Settings.SEARCH_HISTORY, searchFieldCompletion.getTerms());
String title = showname;
if (task.getNumberOfSeason() != SeasonSpinnerEditor.ALL_SEASONS)
title += " - Season " + task.getNumberOfSeason();
if (task.getNumberOfSeason() != SeasonSpinnerEditor.ALL_SEASONS) {
title += String.format(" - Season %d", task.getNumberOfSeason());
}
episodeList.setTitle(title);
FetchEpisodesTask getEpisodesTask = new FetchEpisodesTask(task.getSearchEngine(), showname, task.getNumberOfSeason());
getEpisodesTask.addPropertyChangeListener(new FetchEpisodesTaskListener(episodeList));
FetchEpisodeListTask getEpisodesTask = new FetchEpisodeListTask(task.getSearchEngine(), showname, task.getNumberOfSeason());
getEpisodesTask.addPropertyChangeListener(new FetchEpisodeListTaskListener(episodeList));
getEpisodesTask.execute();
}
}
private class FetchEpisodesTaskListener extends SwingWorkerPropertyChangeAdapter {
private class FetchEpisodeListTaskListener extends SwingWorkerPropertyChangeAdapter {
private EpisodeListPanel episodeList;
public FetchEpisodesTaskListener(EpisodeListPanel episodeList) {
public FetchEpisodeListTaskListener(EpisodeListPanel episodeList) {
this.episodeList = episodeList;
}
@ -367,7 +380,7 @@ public class SearchPanel extends FileBotPanel {
if (tabbedPane.indexOfComponent(episodeList) < 0)
return;
FetchEpisodesTask task = (FetchEpisodesTask) evt.getSource();
FetchEpisodeListTask task = (FetchEpisodeListTask) evt.getSource();
try {
URL url = task.getSearchEngine().getEpisodeListUrl(task.getShowName(), task.getNumberOfSeason());

View File

@ -25,7 +25,7 @@ class ChecksumTableModel extends AbstractTableModel {
private List<File> checksumColumnRoots = new ArrayList<File>();
private int checksumColumnsOffset = 2;
private final int checksumColumnsOffset = 2;
@Override

View File

@ -79,7 +79,9 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
* @param chunks
*/
protected final void publish(V... chunks) {
worker.publishChunks(chunks);
if (worker != null) {
worker.publishChunks(chunks);
}
}

View File

@ -9,10 +9,10 @@ import java.net.URLEncoder;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -26,7 +26,7 @@ import org.xml.sax.SAXException;
public class AnidbClient extends EpisodeListClient {
private Map<String, URL> cache = Collections.synchronizedMap(new HashMap<String, URL>());
private NavigableMap<String, URL> cache = new TreeMap<String, URL>(String.CASE_INSENSITIVE_ORDER);
private String host = "anidb.info";
@ -38,13 +38,17 @@ public class AnidbClient extends EpisodeListClient {
@Override
public List<String> search(String searchterm) throws IOException, SAXException {
if (cache.containsKey(searchterm))
return Arrays.asList(searchterm);
synchronized (cache) {
if (getFoundName(searchterm) != null) {
return Arrays.asList(getFoundName(searchterm));
}
}
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
List<Node> nodes = XPathUtil.selectNodes("//TABLE[@class='anime_list']//TR//TD//ancestor::TR", dom);
ArrayList<String> shows = new ArrayList<String>(nodes.size());
LinkedHashMap<String, URL> searchResults = new LinkedHashMap<String, URL>(nodes.size());
if (!nodes.isEmpty())
for (Node node : nodes) {
@ -62,8 +66,7 @@ public class AnidbClient extends EpisodeListClient {
try {
URL url = new URL("http", host, file);
cache.put(title, url);
shows.add(title);
searchResults.put(title, url);
} catch (MalformedURLException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid href: " + href);
}
@ -71,20 +74,22 @@ public class AnidbClient extends EpisodeListClient {
}
else {
// we might have been redirected to the episode list page directly
List<Node> results = XPathUtil.selectNodes("//TABLE[@class='eplist']", dom);
List<Node> list = XPathUtil.selectNodes("//TABLE[@class='eplist']", dom);
if (!results.isEmpty()) {
if (!list.isEmpty()) {
// get show's name from the document
String header = XPathUtil.selectString("//DIV[@id='layout-content']//H1[1]", dom);
String title = header.replaceFirst("Anime:\\s*", "");
cache.put(title, getSearchUrl(searchterm));
shows.add(title);
searchResults.put(title, getSearchUrl(searchterm));
}
}
return shows;
synchronized (cache) {
cache.putAll(searchResults);
}
return new ArrayList<String>(searchResults.keySet());
}
@ -124,7 +129,21 @@ public class AnidbClient extends EpisodeListClient {
@Override
public URL getEpisodeListUrl(String showname, int season) {
return cache.get(showname);
synchronized (cache) {
return cache.get(showname);
}
}
@Override
public String getFoundName(String searchterm) {
synchronized (cache) {
if (cache.containsKey(searchterm)) {
return cache.floorKey(searchterm);
}
}
return null;
}

View File

@ -42,11 +42,12 @@ public abstract class EpisodeListClient {
public abstract List<String> search(String searchterm) throws Exception;
public abstract String getFoundName(String searchterm);
/**
* @param showname
* @param season number of season, 0 for all seasons
* @return
* @throws Exception
*/
public abstract List<Episode> getEpisodeList(String showname, int season) throws Exception;

View File

@ -9,10 +9,10 @@ import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@ -28,7 +28,7 @@ import org.xml.sax.SAXException;
public class TVRageClient extends EpisodeListClient {
private Map<String, URL> cache = Collections.synchronizedMap(new HashMap<String, URL>());
private NavigableMap<String, URL> cache = new TreeMap<String, URL>(String.CASE_INSENSITIVE_ORDER);
private String host = "www.tvrage.com";
@ -40,15 +40,17 @@ public class TVRageClient extends EpisodeListClient {
@Override
public List<String> search(String searchterm) throws IOException, SAXException {
if (cache.containsKey(searchterm)) {
return Arrays.asList(searchterm);
synchronized (cache) {
if (getFoundName(searchterm) != null) {
return Arrays.asList(getFoundName(searchterm));
}
}
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
List<Node> nodes = XPathUtil.selectNodes("id('search_begin')//TABLE[1]/*/TR/TD/A[1]", dom);
ArrayList<String> shows = new ArrayList<String>(nodes.size());
LinkedHashMap<String, URL> searchResults = new LinkedHashMap<String, URL>(nodes.size());
for (Node node : nodes) {
String href = XPathUtil.selectString("@href", node);
@ -56,14 +58,17 @@ public class TVRageClient extends EpisodeListClient {
try {
URL url = new URL(href);
cache.put(title, url);
shows.add(title);
searchResults.put(title, url);
} catch (MalformedURLException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid href: " + href, e);
}
}
return shows;
synchronized (cache) {
cache.putAll(searchResults);
}
return new ArrayList<String>(searchResults.keySet());
}
@ -100,8 +105,8 @@ public class TVRageClient extends EpisodeListClient {
} else {
episodeNumber = seasonAndEpisodeNumber;
}
episodes.add(new Episode(showname, seasonNumber, episodeNumber, title));
}
}
}
@ -113,21 +118,39 @@ public class TVRageClient extends EpisodeListClient {
@Override
public URL getEpisodeListUrl(String showname, int season) {
try {
URL baseUrl = cache.get(showname);
URL baseUrl = null;
synchronized (cache) {
baseUrl = cache.get(showname);
}
String seasonString = "all";
if (season >= 1)
if (season >= 1) {
seasonString = Integer.toString(season);
}
String file = baseUrl.getFile() + "/episode_list/" + seasonString;
return new URL("http", host, file);
} catch (Exception e) {
throw new RuntimeException("Cannot determine URL of episode listing for " + showname, e);
return null;
}
}
@Override
public String getFoundName(String searchterm) {
synchronized (cache) {
if (cache.containsKey(searchterm)) {
return cache.floorKey(searchterm);
}
}
return null;
}
private URL getSearchUrl(String searchterm) throws UnsupportedEncodingException, MalformedURLException {
String qs = URLEncoder.encode(searchterm, "UTF-8");
String file = "/search.php?search=" + qs;

View File

@ -10,10 +10,10 @@ import java.net.URLEncoder;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -27,7 +27,7 @@ import org.xml.sax.SAXException;
public class TvdotcomClient extends EpisodeListClient {
private Map<String, URL> cache = Collections.synchronizedMap(new HashMap<String, URL>());
private NavigableMap<String, URL> cache = new TreeMap<String, URL>(String.CASE_INSENSITIVE_ORDER);
private String host = "www.tv.com";
@ -39,15 +39,17 @@ public class TvdotcomClient extends EpisodeListClient {
@Override
public List<String> search(String searchterm) throws UnsupportedEncodingException, MalformedURLException, IOException, SAXException {
if (cache.containsKey(searchterm)) {
return Arrays.asList(searchterm);
synchronized (cache) {
if (getFoundName(searchterm) != null) {
return Arrays.asList(getFoundName(searchterm));
}
}
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
List<Node> nodes = XPathUtil.selectNodes("id('search-results')//SPAN/A", dom);
ArrayList<String> shows = new ArrayList<String>(nodes.size());
LinkedHashMap<String, URL> searchResults = new LinkedHashMap<String, URL>(nodes.size());
for (Node node : nodes) {
String category = node.getParentNode().getTextContent();
@ -60,15 +62,18 @@ public class TvdotcomClient extends EpisodeListClient {
try {
URL url = new URL(href);
cache.put(title, url);
shows.add(title);
searchResults.put(title, url);
} catch (MalformedURLException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid href: " + href, e);
}
}
}
return shows;
synchronized (cache) {
cache.putAll(searchResults);
}
return new ArrayList<String>(searchResults.keySet());
}
@ -121,18 +126,34 @@ public class TvdotcomClient extends EpisodeListClient {
@Override
public URL getEpisodeListUrl(String showname, int season) {
try {
String summaryFile = cache.get(showname).getFile();
String summaryFile = null;
synchronized (cache) {
summaryFile = cache.get(showname).getFile();
}
String base = summaryFile.substring(0, summaryFile.indexOf("summary.html"));
String episodelistFile = base + "episode_listings.html&season=" + season;
return new URL("http", host, episodelistFile);
} catch (Exception e) {
throw new RuntimeException("Cannot determine URL of episode listing for " + showname, e);
return null;
}
}
@Override
public String getFoundName(String searchterm) {
synchronized (cache) {
if (cache.containsKey(searchterm)) {
return cache.floorKey(searchterm);
}
}
return null;
}
private URL getSearchUrl(String searchterm) throws UnsupportedEncodingException, MalformedURLException {
String qs = URLEncoder.encode(searchterm, "UTF-8");
String file = "/search.php?qs=" + qs + "&type=11&stype=all";

View File

@ -37,37 +37,37 @@ public class TextCompletion {
}
public void addCompletionTerm(String term) {
public void addTerm(String term) {
completionTerms.add(term);
}
public void addCompletionTerms(Collection<String> terms) {
public void addTerms(Collection<String> terms) {
completionTerms.addAll(terms);
}
public void removeCompletionTerm(String term) {
public void removeTerm(String term) {
completionTerms.remove(term);
}
public void removeCompletionTerms(Collection<String> terms) {
public void removeTerms(Collection<String> terms) {
completionTerms.removeAll(terms);
}
public void setCompletionStartLength(int codeCompletionStartLength) {
public void setStartLength(int codeCompletionStartLength) {
this.completionStartLength = codeCompletionStartLength;
}
public Set<String> getCompletionTerms() {
public Set<String> getTerms() {
return completionTerms;
}
public int getCompletionStartLength() {
public int getStartLength() {
return completionStartLength;
}