* refactor caching

* added caching to tvrage
This commit is contained in:
Reinhard Pointner 2011-11-13 18:22:50 +00:00
parent a0d09d2c83
commit cfee1cbb51
9 changed files with 126 additions and 162 deletions

View File

@ -4,8 +4,15 @@ package net.sourceforge.filebot.web;
import static net.sourceforge.filebot.web.EpisodeUtilities.*; import static net.sourceforge.filebot.web.EpisodeUtilities.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
public abstract class AbstractEpisodeListProvider implements EpisodeListProvider { public abstract class AbstractEpisodeListProvider implements EpisodeListProvider {
@ -54,4 +61,60 @@ public abstract class AbstractEpisodeListProvider implements EpisodeListProvider
return eps; return eps;
} }
protected static class ResultCache {
private final String id;
private final Cache cache;
public ResultCache(String id, Cache cache) {
this.id = id;
this.cache = cache;
}
public void putSearchResult(String key, Collection<? extends SearchResult> value) {
cache.put(new Element(key(id, "SearchResult", key), value.toArray(new SearchResult[0])));
}
public List<SearchResult> getSearchResult(String key) {
try {
Element element = cache.get(key(id, "SearchResult", key));
if (element != null) {
return Arrays.asList(((SearchResult[]) element.getValue()));
}
} catch (Exception e) {
Logger.getLogger(AbstractEpisodeListProvider.class.getName()).log(Level.WARNING, e.getMessage(), e);
}
return null;
}
public void putEpisodeList(int key, Locale language, List<Episode> episodes) {
cache.put(new Element(key(id, "EpisodeList", key, language.getLanguage()), episodes.toArray(new Episode[0])));
}
public List<Episode> getEpisodeList(int key, Locale language) {
try {
Element element = cache.get(key(id, "EpisodeList", key, language.getLanguage()));
if (element != null) {
return Arrays.asList((Episode[]) element.getValue());
}
} catch (Exception e) {
Logger.getLogger(AbstractEpisodeListProvider.class.getName()).log(Level.WARNING, e.getMessage(), e);
}
return null;
}
private String key(Object... key) {
return Arrays.toString(key);
}
}
} }

View File

@ -6,13 +6,10 @@ import static net.sourceforge.filebot.web.EpisodeUtilities.*;
import static net.sourceforge.filebot.web.WebRequest.*; import static net.sourceforge.filebot.web.WebRequest.*;
import static net.sourceforge.tuned.XPathUtilities.*; import static net.sourceforge.tuned.XPathUtilities.*;
import java.io.Serializable;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -29,16 +26,14 @@ import javax.swing.Icon;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager; import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
public class AnidbClient extends AbstractEpisodeListProvider { public class AnidbClient extends AbstractEpisodeListProvider {
private static final String host = "anidb.net"; private final String host = "anidb.net";
private static final AnidbCache cache = new AnidbCache(CacheManager.getInstance().getCache("web-persistent-datasource")); private final ResultCache cache = new ResultCache(host, CacheManager.getInstance().getCache("web-persistent-datasource"));
private final String client; private final String client;
private final int clientver; private final int clientver;
@ -96,7 +91,7 @@ public class AnidbClient extends AbstractEpisodeListProvider {
URL url = new URL("http", "api." + host, 9001, "/httpapi?request=anime&client=" + client + "&clientver=" + clientver + "&protover=1&aid=" + anime.getAnimeId()); URL url = new URL("http", "api." + host, 9001, "/httpapi?request=anime&client=" + client + "&clientver=" + clientver + "&protover=1&aid=" + anime.getAnimeId());
// try cache first // try cache first
List<Episode> episodes = cache.getEpisodeList(anime.getAnimeId(), language.getLanguage()); List<Episode> episodes = cache.getEpisodeList(anime.getAnimeId(), language);
if (episodes != null) if (episodes != null)
return episodes; return episodes;
@ -134,7 +129,7 @@ public class AnidbClient extends AbstractEpisodeListProvider {
// sanity check // sanity check
if (episodes.size() > 0) { if (episodes.size() > 0) {
// populate cache // populate cache
cache.putEpisodeList(episodes, anime.getAnimeId(), language.getLanguage()); cache.putEpisodeList(anime.getAnimeId(), language, episodes);
} else { } else {
// anime page xml doesn't work sometimes // anime page xml doesn't work sometimes
throw new RuntimeException(String.format("Failed to parse episode data from xml: %s (%d)", anime, anime.getAnimeId())); throw new RuntimeException(String.format("Failed to parse episode data from xml: %s (%d)", anime, anime.getAnimeId()));
@ -171,8 +166,8 @@ public class AnidbClient extends AbstractEpisodeListProvider {
protected List<AnidbSearchResult> getAnimeTitles() throws Exception { protected List<AnidbSearchResult> getAnimeTitles() throws Exception {
URL url = new URL("http", host, "/api/animetitles.dat.gz"); URL url = new URL("http", host, "/api/animetitles.dat.gz");
// try cache first @SuppressWarnings("unchecked")
List<AnidbSearchResult> anime = cache.getAnimeList(); List<AnidbSearchResult> anime = (List) cache.getSearchResult(null);
if (anime != null) if (anime != null)
return anime; return anime;
@ -221,13 +216,13 @@ public class AnidbClient extends AbstractEpisodeListProvider {
} }
// populate cache // populate cache
cache.putAnimeList(anime); cache.putSearchResult(null, anime);
return anime; return anime;
} }
public static class AnidbSearchResult extends SearchResult implements Serializable { public static class AnidbSearchResult extends SearchResult {
protected int aid; protected int aid;
protected String primaryTitle; // one per anime protected String primaryTitle; // one per anime
@ -267,46 +262,4 @@ public class AnidbClient extends AbstractEpisodeListProvider {
} }
} }
private static class AnidbCache {
private final Cache cache;
public AnidbCache(Cache cache) {
this.cache = cache;
}
public void putAnimeList(Collection<AnidbSearchResult> anime) {
cache.put(new Element(host + "AnimeList", anime.toArray(new AnidbSearchResult[0])));
}
public List<AnidbSearchResult> getAnimeList() {
Element element = cache.get(host + "AnimeList");
if (element != null)
return Arrays.asList((AnidbSearchResult[]) element.getValue());
return null;
}
public void putEpisodeList(Collection<Episode> episodes, int aid, String lang) {
cache.put(new Element(host + "EpisodeList" + aid + lang, episodes.toArray(new Episode[0])));
}
public List<Episode> getEpisodeList(int aid, String lang) {
Element element = cache.get(host + "EpisodeList" + aid + lang);
if (element != null)
return Arrays.asList((Episode[]) element.getValue());
return null;
}
}
} }

View File

@ -28,7 +28,7 @@ import net.sourceforge.filebot.ResourceManager;
public class IMDbClient extends AbstractEpisodeListProvider { public class IMDbClient extends AbstractEpisodeListProvider {
private static final String host = "www.imdb.com"; private final String host = "www.imdb.com";
@Override @Override

View File

@ -2,7 +2,10 @@
package net.sourceforge.filebot.web; package net.sourceforge.filebot.web;
public abstract class SearchResult { import java.io.Serializable;
public abstract class SearchResult implements Serializable {
protected final String name; protected final String name;

View File

@ -7,12 +7,9 @@ import static net.sourceforge.filebot.web.WebRequest.*;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.Serializable;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
@ -24,16 +21,14 @@ import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager; import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
public class SerienjunkiesClient extends AbstractEpisodeListProvider { public class SerienjunkiesClient extends AbstractEpisodeListProvider {
private static final String host = "api.serienjunkies.de"; private final String host = "api.serienjunkies.de";
private static final SerienjunkiesCache cache = new SerienjunkiesCache(CacheManager.getInstance().getCache("web-persistent-datasource")); private final ResultCache cache = new ResultCache(host, CacheManager.getInstance().getCache("web-datasource"));
private final String apikey; private final String apikey;
@ -76,8 +71,8 @@ public class SerienjunkiesClient extends AbstractEpisodeListProvider {
protected List<SerienjunkiesSearchResult> getSeriesTitles() throws IOException { protected List<SerienjunkiesSearchResult> getSeriesTitles() throws IOException {
// try cache first @SuppressWarnings("unchecked")
List<SerienjunkiesSearchResult> seriesList = cache.getSeriesList(); List<SerienjunkiesSearchResult> seriesList = (List) cache.getSearchResult(null);
if (seriesList != null) if (seriesList != null)
return seriesList; return seriesList;
@ -100,7 +95,7 @@ public class SerienjunkiesClient extends AbstractEpisodeListProvider {
} }
// populate cache // populate cache
cache.putSeriesList(seriesList); cache.putSearchResult(null, seriesList);
return seriesList; return seriesList;
} }
@ -111,7 +106,7 @@ public class SerienjunkiesClient extends AbstractEpisodeListProvider {
SerienjunkiesSearchResult series = (SerienjunkiesSearchResult) searchResult; SerienjunkiesSearchResult series = (SerienjunkiesSearchResult) searchResult;
// try cache first // try cache first
List<Episode> episodes = cache.getEpisodeList(series.getSeriesId()); List<Episode> episodes = cache.getEpisodeList(series.getSeriesId(), Locale.GERMAN);
if (episodes != null) if (episodes != null)
return episodes; return episodes;
@ -134,7 +129,7 @@ public class SerienjunkiesClient extends AbstractEpisodeListProvider {
} }
// populate cache // populate cache
cache.putEpisodeList(episodes, series.getSeriesId()); cache.putEpisodeList(series.getSeriesId(), Locale.GERMAN, episodes);
// make sure episodes are in ordered correctly // make sure episodes are in ordered correctly
sortEpisodes(episodes); sortEpisodes(episodes);
@ -177,7 +172,7 @@ public class SerienjunkiesClient extends AbstractEpisodeListProvider {
} }
public static class SerienjunkiesSearchResult extends SearchResult implements Serializable { public static class SerienjunkiesSearchResult extends SearchResult {
protected int sid; protected int sid;
protected String link; protected String link;
@ -231,46 +226,4 @@ public class SerienjunkiesClient extends AbstractEpisodeListProvider {
} }
} }
private static class SerienjunkiesCache {
private final Cache cache;
public SerienjunkiesCache(Cache cache) {
this.cache = cache;
}
public void putSeriesList(Collection<SerienjunkiesSearchResult> anime) {
cache.put(new Element(host + "SeriesList", anime.toArray(new SerienjunkiesSearchResult[0])));
}
public List<SerienjunkiesSearchResult> getSeriesList() {
Element element = cache.get(host + "SeriesList");
if (element != null)
return Arrays.asList((SerienjunkiesSearchResult[]) element.getValue());
return null;
}
public void putEpisodeList(Collection<Episode> episodes, int sid) {
cache.put(new Element(host + "EpisodeList" + sid, episodes.toArray(new Episode[0])));
}
public List<Episode> getEpisodeList(int sid) {
Element element = cache.get(host + "EpisodeList" + sid);
if (element != null)
return Arrays.asList((Episode[]) element.getValue());
return null;
}
}
} }

View File

@ -19,12 +19,14 @@ import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import net.sf.ehcache.CacheManager;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
public class TVRageClient extends AbstractEpisodeListProvider { public class TVRageClient extends AbstractEpisodeListProvider {
private static final String host = "services.tvrage.com"; private final String host = "services.tvrage.com";
private final ResultCache cache = new ResultCache(host, CacheManager.getInstance().getCache("web-datasource"));
@Override @Override
@ -41,13 +43,10 @@ public class TVRageClient extends AbstractEpisodeListProvider {
@Override @Override
public List<SearchResult> search(String query, Locale locale) throws IOException, SAXException { public List<SearchResult> search(String query, Locale locale) throws IOException, SAXException {
URL searchUrl = new URL("http", host, "/feeds/full_search.php?show=" + encode(query)); URL searchUrl = new URL("http", host, "/feeds/full_search.php?show=" + encode(query));
Document dom = getDocument(searchUrl); Document dom = getDocument(searchUrl);
List<Node> nodes = selectNodes("Results/show", dom); List<Node> nodes = selectNodes("Results/show", dom);
List<SearchResult> searchResults = new ArrayList<SearchResult>(nodes.size()); List<SearchResult> searchResults = new ArrayList<SearchResult>(nodes.size());
for (Node node : nodes) { for (Node node : nodes) {
@ -64,15 +63,19 @@ public class TVRageClient extends AbstractEpisodeListProvider {
@Override @Override
public List<Episode> getEpisodeList(SearchResult searchResult, Locale locale) throws IOException, SAXException { public List<Episode> getEpisodeList(SearchResult searchResult, Locale locale) throws IOException, SAXException {
int showId = ((TVRageSearchResult) searchResult).getShowId(); TVRageSearchResult series = (TVRageSearchResult) searchResult;
URL episodeListUrl = new URL("http", host, "/feeds/full_show_info.php?sid=" + showId);
List<Episode> episodes = cache.getEpisodeList(series.getSeriesId(), Locale.ENGLISH);
if (episodes != null)
return episodes;
URL episodeListUrl = new URL("http", host, "/feeds/full_show_info.php?sid=" + series.getSeriesId());
Document dom = getDocument(episodeListUrl); Document dom = getDocument(episodeListUrl);
String seriesName = selectString("Show/name", dom); String seriesName = selectString("Show/name", dom);
Date seriesStartDate = Date.parse(selectString("Show/started", dom), "MMM/dd/yyyy"); Date seriesStartDate = Date.parse(selectString("Show/started", dom), "MMM/dd/yyyy");
List<Episode> episodes = new ArrayList<Episode>(25); episodes = new ArrayList<Episode>(25);
List<Episode> specials = new ArrayList<Episode>(5); List<Episode> specials = new ArrayList<Episode>(5);
// episodes and specials // episodes and specials
@ -98,6 +101,7 @@ public class TVRageClient extends AbstractEpisodeListProvider {
// add specials at the end // add specials at the end
episodes.addAll(specials); episodes.addAll(specials);
cache.putEpisodeList(series.getSeriesId(), Locale.ENGLISH, episodes);
return episodes; return episodes;
} }
@ -134,7 +138,7 @@ public class TVRageClient extends AbstractEpisodeListProvider {
} }
public int getShowId() { public int getSeriesId() {
return showId; return showId;
} }

View File

@ -10,7 +10,6 @@ import java.io.FileNotFoundException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -29,21 +28,18 @@ import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager; import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
public class TheTVDBClient extends AbstractEpisodeListProvider { public class TheTVDBClient extends AbstractEpisodeListProvider {
private static final String host = "www.thetvdb.com"; private final String host = "www.thetvdb.com";
private final String apikey;
private final Map<MirrorType, String> mirrors = new EnumMap<MirrorType, String>(MirrorType.class); private final Map<MirrorType, String> mirrors = new EnumMap<MirrorType, String>(MirrorType.class);
private final ResultCache cache = new ResultCache(host, CacheManager.getInstance().getCache("web-datasource"));
private final TheTVDBCache cache = new TheTVDBCache(CacheManager.getInstance().getCache("web-datasource")); private final String apikey;
public TheTVDBClient(String apikey) { public TheTVDBClient(String apikey) {
@ -315,37 +311,4 @@ public class TheTVDBClient extends AbstractEpisodeListProvider {
} }
private static class TheTVDBCache {
private final Cache cache;
public TheTVDBCache(Cache cache) {
this.cache = cache;
}
public void putEpisodeList(int seriesId, Locale language, List<Episode> episodes) {
cache.put(new Element(key(host, "EpisodeList", seriesId, language.getLanguage()), episodes));
}
@SuppressWarnings("unchecked")
public List<Episode> getEpisodeList(int seriesId, Locale language) {
Element element = cache.get(key(host, "EpisodeList", seriesId, language.getLanguage()));
if (element != null)
return (List<Episode>) element.getValue();
return null;
}
private String key(Object... key) {
return Arrays.toString(key);
}
}
} }

View File

@ -6,9 +6,14 @@ import static net.sourceforge.filebot.ui.rename.MatchSimilarityMetric.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.similarity.Matcher;
import net.sourceforge.filebot.similarity.SimilarityMetric;
import net.sourceforge.filebot.web.Date; import net.sourceforge.filebot.web.Date;
import net.sourceforge.filebot.web.Episode; import net.sourceforge.filebot.web.Episode;
@ -18,7 +23,7 @@ public class MatchSimilarityMetricTest {
@Test @Test
public void substringMetrics() { public void substringMetrics() {
Episode eY1T1 = new Episode("Doctor Who", new Date(2005, 0, 0), 1, 1, "Rose"); Episode eY1T1 = new Episode("Doctor Who", new Date(2005, 0, 0), 1, 1, "Rose");
Episode eY2T2 = new Episode("Doctor Who", new Date(1963, 0, 0), 1, 1, "An Unearthly Child"); // Episode eY2T2 = new Episode("Doctor Who", new Date(1963, 0, 0), 1, 1, "An Unearthly Child");
File fY1T1 = new File("Doctor Who (2005)/Doctor Who - 1x01 - Rose"); File fY1T1 = new File("Doctor Who (2005)/Doctor Who - 1x01 - Rose");
File fY2T2 = new File("Doctor Who (1963)/Doctor Who - 1x01 - An Unearthly Child"); File fY2T2 = new File("Doctor Who (1963)/Doctor Who - 1x01 - An Unearthly Child");
@ -44,4 +49,24 @@ public class MatchSimilarityMetricTest {
assertEquals("abc", MatchSimilarityMetric.normalizeObject(new File("/folder/abc[EF62DF13].txt"))); assertEquals("abc", MatchSimilarityMetric.normalizeObject(new File("/folder/abc[EF62DF13].txt")));
} }
@Test
public void matcherLevel2() throws Exception {
List<File> files = new ArrayList<File>();
List<Episode> episodes = new ArrayList<Episode>();
files.add(new File("Greek/Greek - S01E19 - No Campus for Old Rules"));
files.add(new File("Veronica Mars - Season 1/Veronica Mars [1x19] Hot Dogs"));
episodes.add(new Episode("Veronica Mars", null, 1, 19, "Hot Dogs"));
episodes.add(new Episode("Greek", null, 1, 19, "No Campus for Old Rules"));
SimilarityMetric[] metrics = new SimilarityMetric[] { EpisodeIdentifier, Title };
List<Match<File, Episode>> m = new Matcher<File, Episode>(files, episodes, true, metrics).match();
assertEquals("Greek - S01E19 - No Campus for Old Rules", m.get(0).getValue().getName());
assertEquals("Greek - 1x19 - No Campus for Old Rules", m.get(0).getCandidate().toString());
assertEquals("Veronica Mars [1x19] Hot Dogs", m.get(1).getValue().getName());
assertEquals("Veronica Mars - 1x19 - Hot Dogs", m.get(1).getCandidate().toString());
}
} }

View File

@ -26,7 +26,7 @@ public class TVRageClientTest {
TVRageSearchResult result = (TVRageSearchResult) results.get(0); TVRageSearchResult result = (TVRageSearchResult) results.get(0);
assertEquals(buffySearchResult.getName(), result.getName()); assertEquals(buffySearchResult.getName(), result.getName());
assertEquals(buffySearchResult.getShowId(), result.getShowId()); assertEquals(buffySearchResult.getSeriesId(), result.getSeriesId());
assertEquals(buffySearchResult.getLink(), result.getLink()); assertEquals(buffySearchResult.getLink(), result.getLink());
} }