* refactored anidb client a bit (added internal support for multi-language episode titles)

This commit is contained in:
Reinhard Pointner 2010-11-11 14:23:59 +00:00
parent 270a0409ca
commit 038dcb38e8
2 changed files with 83 additions and 28 deletions

View File

@ -5,9 +5,7 @@ package net.sourceforge.filebot.web;
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.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
@ -15,16 +13,17 @@ import java.net.URLConnection;
import java.util.AbstractList; import java.util.AbstractList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.AbstractMap.SimpleEntry; import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@ -33,7 +32,6 @@ 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 org.xml.sax.SAXException;
import uk.ac.shef.wit.simmetrics.similaritymetrics.AbstractStringMetric; import uk.ac.shef.wit.simmetrics.similaritymetrics.AbstractStringMetric;
import uk.ac.shef.wit.simmetrics.similaritymetrics.QGramsDistance; import uk.ac.shef.wit.simmetrics.similaritymetrics.QGramsDistance;
@ -48,7 +46,7 @@ public class AnidbClient implements EpisodeListProvider {
private static final String host = "anidb.net"; private static final String host = "anidb.net";
private static final Cache cache = CacheManager.getInstance().getCache("anidb"); private static final AnidbCache cache = new AnidbCache(CacheManager.getInstance().getCache("anidb"));
@Override @Override
@ -64,7 +62,7 @@ public class AnidbClient implements EpisodeListProvider {
@Override @Override
public List<SearchResult> search(String query) throws IOException, SAXException { public List<SearchResult> search(String query) throws Exception {
// normalize // normalize
query = query.toLowerCase(); query = query.toLowerCase();
@ -117,16 +115,18 @@ public class AnidbClient implements EpisodeListProvider {
@Override @Override
public List<Episode> getEpisodeList(SearchResult searchResult) throws IOException, SAXException { public List<Episode> getEpisodeList(SearchResult searchResult) throws Exception {
int aid = ((AnidbSearchResult) searchResult).getAnimeId(); return getEpisodeList((AnidbSearchResult) searchResult, Locale.ENGLISH);
URL url = new URL("http", host, "/perl-bin/animedb.pl?show=xml&t=anime&aid=" + aid); }
public List<Episode> getEpisodeList(AnidbSearchResult anime, Locale language) throws Exception {
URL url = new URL("http", host, "/perl-bin/animedb.pl?show=xml&t=anime&aid=" + anime.getAnimeId());
// try cache first // try cache first
try { List<Episode> episodes = cache.getEpisodeList(anime.getAnimeId(), language.getLanguage());
return Arrays.asList((Episode[]) cache.get(url.toString()).getValue()); if (episodes != null)
} catch (Exception e) { return episodes;
// ignore
}
// set request headers to resemble an ajax request // set request headers to resemble an ajax request
URLConnection connection = url.openConnection(); URLConnection connection = url.openConnection();
@ -138,14 +138,14 @@ public class AnidbClient implements EpisodeListProvider {
// select main title // select main title
String animeTitle = selectString("//title[@type='main']", dom); String animeTitle = selectString("//title[@type='main']", dom);
List<Episode> episodes = new ArrayList<Episode>(25); episodes = new ArrayList<Episode>(25);
for (Node node : selectNodes("//ep", dom)) { for (Node node : selectNodes("//ep", dom)) {
Integer number = getIntegerContent("epno", node); Integer number = getIntegerContent("epno", node);
// ignore special episodes // ignore special episodes
if (number != null) { if (number != null) {
String title = selectString(".//title[@lang='en']", node); String title = selectString(".//title[@lang='" + language.getLanguage() + "']", node);
String airdate = selectString(".//date/@rel", node); String airdate = selectString(".//date/@rel", node);
// no seasons for anime // no seasons for anime
@ -156,10 +156,10 @@ public class AnidbClient implements EpisodeListProvider {
// sanity check // sanity check
if (episodes.size() > 0) { if (episodes.size() > 0) {
// populate cache // populate cache
cache.put(new Element(url.toString(), episodes.toArray(new Episode[0]))); cache.putEpisodeList(episodes, anime.getAnimeId(), language.getLanguage());
} else { } else {
// anime page xml doesn't work sometimes // anime page xml doesn't work sometimes
Logger.getLogger(getClass().getName()).warning(String.format("Failed to parse episode data from xml: %s (%d)", searchResult, aid)); throw new RuntimeException(String.format("Failed to parse episode data from xml: %s (%d)", anime, anime.getAnimeId()));
} }
return episodes; return episodes;
@ -196,15 +196,13 @@ public class AnidbClient implements EpisodeListProvider {
} }
private AnidbSearchResult[] getAnimeTitles() throws MalformedURLException, IOException, SAXException { private 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 // try cache first
try { List<AnidbSearchResult> anime = cache.getAnimeList();
return (AnidbSearchResult[]) cache.get(url.toString()).getValue(); if (anime != null)
} catch (Exception e) { return anime;
// ignore
}
// <aid>|<type>|<language>|<title> // <aid>|<type>|<language>|<title>
// type: 1=primary title (one per anime), 2=synonyms (multiple per anime), 3=shorttitles (multiple per anime), 4=official title (one per language) // type: 1=primary title (one per anime), 2=synonyms (multiple per anime), 3=shorttitles (multiple per anime), 4=official title (one per language)
@ -232,17 +230,17 @@ public class AnidbClient implements EpisodeListProvider {
scanner.close(); scanner.close();
} }
List<AnidbSearchResult> anime = new ArrayList<AnidbSearchResult>(primaryTitleMap.size()); // build up a list of all possible anidb search results
anime = new ArrayList<AnidbSearchResult>(primaryTitleMap.size());
for (Entry<Integer, String> entry : primaryTitleMap.entrySet()) { for (Entry<Integer, String> entry : primaryTitleMap.entrySet()) {
anime.add(new AnidbSearchResult(entry.getKey(), entry.getValue(), englishTitleMap.get(entry.getKey()))); anime.add(new AnidbSearchResult(entry.getKey(), entry.getValue(), englishTitleMap.get(entry.getKey())));
} }
// populate cache // populate cache
AnidbSearchResult[] result = anime.toArray(new AnidbSearchResult[0]); cache.putAnimeList(anime);
cache.put(new Element(url.toString(), result));
return result; return anime;
} }
@ -286,4 +284,46 @@ public class AnidbClient implements EpisodeListProvider {
} }
} }
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

@ -5,6 +5,7 @@ package net.sourceforge.filebot.web;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.List; import java.util.List;
import java.util.Locale;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -113,6 +114,20 @@ public class AnidbClientTest {
} }
@Test
public void getEpisodeListI18N() throws Exception {
List<Episode> list = anidb.getEpisodeList(monsterSearchResult, Locale.JAPANESE);
Episode last = list.get(73);
assertEquals("Monster", last.getSeriesName());
assertEquals("本当の怪物", last.getTitle());
assertEquals("74", last.getEpisode().toString());
assertEquals("74", last.getAbsolute().toString());
assertEquals(null, last.getSeason());
assertEquals("2005-09-28", last.airdate().toString());
}
@Test @Test
public void getEpisodeListTrimRecap() throws Exception { public void getEpisodeListTrimRecap() throws Exception {
assertEquals("Sea God of the East, Azure Sea of the West - Transition Chapter", anidb.getEpisodeList(twelvekingdomsSearchResult).get(44).getTitle()); assertEquals("Sea God of the East, Azure Sea of the West - Transition Chapter", anidb.getEpisodeList(twelvekingdomsSearchResult).get(44).getTitle());