* added initial support for serienjunkies as episode list provider
This commit is contained in:
parent
e163824bd0
commit
d499bb01d6
|
@ -2,7 +2,7 @@
|
|||
<project name="FileBot" default="fatjar">
|
||||
|
||||
<property name="title" value="${ant.project.name}" />
|
||||
<property name="version" value="1.96.471" />
|
||||
<property name="version" value="1.97" />
|
||||
|
||||
<tstamp>
|
||||
<format property="today" pattern="yyyy-MM-dd" />
|
||||
|
@ -65,6 +65,10 @@
|
|||
<include name="org/cyberneko/html/**" />
|
||||
</zipfileset>
|
||||
|
||||
<zipfileset src="${dir.lib}/json-simple.jar">
|
||||
<include name="org/json/simple/**" />
|
||||
</zipfileset>
|
||||
|
||||
<zipfileset src="${dir.lib}/simmetrics.jar">
|
||||
<include name="uk/ac/shef/wit/simmetrics/**" />
|
||||
</zipfileset>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
Binary file not shown.
|
@ -133,11 +133,11 @@
|
|||
/>
|
||||
|
||||
<!--
|
||||
Very long-lived cache (one month!) for AniDB anime list and episode information.
|
||||
Very long-lived cache (one month!) for AniDB and Serienjunkies anime list and episode information.
|
||||
-->
|
||||
<cache name="anidb"
|
||||
<cache name="web-persistent-datasource"
|
||||
maxElementsInMemory="20"
|
||||
maxElementsOnDisk="120"
|
||||
maxElementsOnDisk="240"
|
||||
eternal="false"
|
||||
timeToIdleSeconds="2628000"
|
||||
timeToLiveSeconds="2628000"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# application settings
|
||||
application.name: FileBot
|
||||
application.version: 1.96
|
||||
application.version: 1.97
|
||||
|
||||
thetvdb.apikey: 58B4AA94C59AD656
|
||||
themoviedb.apikey: 66308fb6e3fd850dde4c7d21df2e8306
|
||||
sublight.apikey: afa9ecb2-a3ee-42b1-9225-000b4038bc85
|
||||
serienjunkies.apikey: 9fbhw9uebfiwvbefzuwv
|
||||
|
|
|
@ -8,6 +8,7 @@ import net.sourceforge.filebot.web.AnidbClient;
|
|||
import net.sourceforge.filebot.web.EpisodeListProvider;
|
||||
import net.sourceforge.filebot.web.IMDbClient;
|
||||
import net.sourceforge.filebot.web.OpenSubtitlesClient;
|
||||
import net.sourceforge.filebot.web.SerienjunkiesClient;
|
||||
import net.sourceforge.filebot.web.SublightSubtitleClient;
|
||||
import net.sourceforge.filebot.web.SubsceneSubtitleClient;
|
||||
import net.sourceforge.filebot.web.SubtitleProvider;
|
||||
|
@ -28,6 +29,7 @@ public final class WebServices {
|
|||
public static final TVDotComClient TVDotCom = new TVDotComClient();
|
||||
public static final IMDbClient IMDb = new IMDbClient();
|
||||
public static final TheTVDBClient TheTVDB = new TheTVDBClient(getApplicationProperty("thetvdb.apikey"));
|
||||
public static final SerienjunkiesClient Serienjunkies = new SerienjunkiesClient(getApplicationProperty("serienjunkies.apikey"));
|
||||
|
||||
// subtitle dbs
|
||||
public static final OpenSubtitlesClient OpenSubtitles = new OpenSubtitlesClient(String.format("%s %s", getApplicationName(), getApplicationVersion()));
|
||||
|
@ -36,7 +38,7 @@ public final class WebServices {
|
|||
|
||||
|
||||
public static EpisodeListProvider[] getEpisodeListProviders() {
|
||||
return new EpisodeListProvider[] { TVRage, AniDB, TVDotCom, IMDb, TheTVDB };
|
||||
return new EpisodeListProvider[] { TVRage, AniDB, TVDotCom, IMDb, TheTVDB, Serienjunkies };
|
||||
}
|
||||
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 660 B |
|
@ -45,8 +45,7 @@ import net.sourceforge.filebot.ResourceManager;
|
|||
public class AnidbClient implements EpisodeListProvider {
|
||||
|
||||
private static final String host = "anidb.net";
|
||||
|
||||
private static final AnidbCache cache = new AnidbCache(CacheManager.getInstance().getCache("anidb"));
|
||||
private static final AnidbCache cache = new AnidbCache(CacheManager.getInstance().getCache("web-persistent-datasource"));
|
||||
|
||||
private final String client;
|
||||
private final int clientver;
|
||||
|
@ -205,7 +204,7 @@ public class AnidbClient implements EpisodeListProvider {
|
|||
}
|
||||
|
||||
|
||||
private List<AnidbSearchResult> getAnimeTitles() throws Exception {
|
||||
protected List<AnidbSearchResult> getAnimeTitles() throws Exception {
|
||||
URL url = new URL("http", host, "/api/animetitles.dat.gz");
|
||||
|
||||
// try cache first
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.web.EpisodeListUtilities.*;
|
||||
import static net.sourceforge.filebot.web.WebRequest.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.swing.Icon;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import uk.ac.shef.wit.simmetrics.similaritymetrics.AbstractStringMetric;
|
||||
import uk.ac.shef.wit.simmetrics.similaritymetrics.QGramsDistance;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
|
||||
|
||||
public class SerienjunkiesClient implements EpisodeListProvider {
|
||||
|
||||
private static final String host = "api.serienjunkies.de";
|
||||
private static final SerienjunkiesCache cache = new SerienjunkiesCache(CacheManager.getInstance().getCache("web-persistent-datasource"));
|
||||
|
||||
private final String apikey;
|
||||
|
||||
|
||||
public SerienjunkiesClient(String apikey) {
|
||||
this.apikey = apikey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<SearchResult> search(String query) throws Exception {
|
||||
// normalize
|
||||
query = query.toLowerCase();
|
||||
|
||||
AbstractStringMetric metric = new QGramsDistance();
|
||||
|
||||
final List<Entry<SearchResult, Float>> resultSet = new ArrayList<Entry<SearchResult, Float>>();
|
||||
|
||||
for (SerienjunkiesSearchResult anime : getSeriesTitles()) {
|
||||
for (String name : new String[] { anime.getMainTitle(), anime.getGermanTitle() }) {
|
||||
if (name != null) {
|
||||
// normalize
|
||||
name = name.toLowerCase();
|
||||
|
||||
float similarity = metric.getSimilarity(name, query);
|
||||
|
||||
if (similarity > 0.5 || name.contains(query)) {
|
||||
resultSet.add(new SimpleEntry<SearchResult, Float>(anime, similarity));
|
||||
|
||||
// add only once
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort by similarity descending (best matches first)
|
||||
Collections.sort(resultSet, new Comparator<Entry<SearchResult, Float>>() {
|
||||
|
||||
@Override
|
||||
public int compare(Entry<SearchResult, Float> o1, Entry<SearchResult, Float> o2) {
|
||||
return o2.getValue().compareTo(o1.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
// view for the first 20 search results
|
||||
return new AbstractList<SearchResult>() {
|
||||
|
||||
@Override
|
||||
public SearchResult get(int index) {
|
||||
return resultSet.get(index).getKey();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return Math.min(20, resultSet.size());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
protected List<SerienjunkiesSearchResult> getSeriesTitles() throws Exception {
|
||||
// try cache first
|
||||
List<SerienjunkiesSearchResult> seriesList = cache.getSeriesList();
|
||||
if (seriesList != null)
|
||||
return seriesList;
|
||||
|
||||
// fetch series data
|
||||
Reader reader = getReader(createConnection("allseries.php?d=" + apikey));
|
||||
seriesList = new ArrayList<SerienjunkiesSearchResult>();
|
||||
|
||||
try {
|
||||
JSONObject data = (JSONObject) JSONValue.parse(reader);
|
||||
JSONArray list = (JSONArray) data.get("allseries");
|
||||
|
||||
for (Object element : list) {
|
||||
JSONObject obj = (JSONObject) element;
|
||||
|
||||
String sid = (String) obj.get("id");
|
||||
String mainTitle = (String) obj.get("short");
|
||||
String germanTitle = (String) obj.get("short_german");
|
||||
|
||||
seriesList.add(new SerienjunkiesSearchResult(Integer.parseInt(sid), mainTitle, germanTitle != null && germanTitle.length() > 0 ? germanTitle : null));
|
||||
}
|
||||
|
||||
// populate cache
|
||||
cache.putSeriesList(seriesList);
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
return seriesList;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Episode> getEpisodeList(SearchResult searchResult) throws Exception {
|
||||
SerienjunkiesSearchResult series = (SerienjunkiesSearchResult) searchResult;
|
||||
|
||||
// try cache first
|
||||
List<Episode> episodes = cache.getEpisodeList(series.getSeriesId());
|
||||
if (episodes != null)
|
||||
return episodes;
|
||||
|
||||
// fetch series data
|
||||
Reader reader = getReader(createConnection("allepisodes.php?d=" + apikey + "&q=" + series.getSeriesId()));
|
||||
episodes = new ArrayList<Episode>(25);
|
||||
|
||||
try {
|
||||
JSONObject data = (JSONObject) JSONValue.parse(reader);
|
||||
JSONArray list = (JSONArray) data.get("allepisodes");
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
JSONObject obj = (JSONObject) list.get(i);
|
||||
|
||||
String season = (String) obj.get("season");
|
||||
String episode = (String) obj.get("episode");
|
||||
String title = (String) obj.get("german");
|
||||
|
||||
episodes.add(new Episode(series.getName(), new Integer(season), new Integer(episode), title, i + 1, null, null));
|
||||
}
|
||||
|
||||
// populate cache
|
||||
cache.putEpisodeList(episodes, series.getSeriesId());
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
// make sure episodes are in ordered correctly
|
||||
sortEpisodes(episodes);
|
||||
|
||||
return episodes;
|
||||
}
|
||||
|
||||
|
||||
private HttpsURLConnection createConnection(String resource) throws IOException, GeneralSecurityException {
|
||||
URL url = new URL("https", host, resource);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
|
||||
// disable SSL certificate validation
|
||||
connection.setSSLSocketFactory(createIgnoreCertificateSocketFactory());
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Episode> getEpisodeList(SearchResult searchResult, int season) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public URI getEpisodeListLink(SearchResult searchResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public URI getEpisodeListLink(SearchResult searchResult, int season) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Serienjunkies";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return ResourceManager.getIcon("search.serienjunkies");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasSingleSeasonSupport() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static class SerienjunkiesSearchResult extends SearchResult implements Serializable {
|
||||
|
||||
protected int sid;
|
||||
protected String mainTitle;
|
||||
protected String germanTitle;
|
||||
|
||||
|
||||
protected SerienjunkiesSearchResult() {
|
||||
// used by serializer
|
||||
}
|
||||
|
||||
|
||||
public SerienjunkiesSearchResult(int sid, String mainTitle, String germanTitle) {
|
||||
this.sid = sid;
|
||||
this.mainTitle = mainTitle;
|
||||
this.germanTitle = germanTitle;
|
||||
}
|
||||
|
||||
|
||||
public int getSeriesId() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return germanTitle != null ? germanTitle : mainTitle; // prefer german title
|
||||
}
|
||||
|
||||
|
||||
public String getMainTitle() {
|
||||
return mainTitle;
|
||||
}
|
||||
|
||||
|
||||
public String getGermanTitle() {
|
||||
return germanTitle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,9 @@ import java.net.URLConnection;
|
|||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
|
@ -24,6 +27,10 @@ import java.util.zip.GZIPInputStream;
|
|||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
|
@ -219,6 +226,29 @@ public final class WebRequest {
|
|||
}
|
||||
|
||||
|
||||
public static SSLSocketFactory createIgnoreCertificateSocketFactory() throws GeneralSecurityException {
|
||||
// create a trust manager that does not validate certificate chains
|
||||
TrustManager trustAnyCertificate = new X509TrustManager() {
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
};
|
||||
|
||||
SSLContext sc = SSLContext.getInstance("SSL");
|
||||
sc.init(null, new TrustManager[] { trustAnyCertificate }, new SecureRandom());
|
||||
return sc.getSocketFactory();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy constructor to prevent instantiation.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sourceforge.filebot.web.SerienjunkiesClient.SerienjunkiesSearchResult;
|
||||
|
||||
|
||||
public class SerienjunkiesClientTest {
|
||||
|
||||
private SerienjunkiesClient serienjunkies = new SerienjunkiesClient("9fbhw9uebfiwvbefzuwv");
|
||||
|
||||
|
||||
@Test
|
||||
public void search() throws Exception {
|
||||
List<SearchResult> results = serienjunkies.search("alias die agentin");
|
||||
assertEquals(1, results.size());
|
||||
|
||||
SerienjunkiesSearchResult first = (SerienjunkiesSearchResult) results.get(0);
|
||||
assertEquals(34, first.getSeriesId());
|
||||
assertEquals("Alias - Die Agentin", first.getName());
|
||||
assertEquals("Alias", first.getMainTitle());
|
||||
assertEquals("Alias - Die Agentin", first.getGermanTitle());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getEpisodeListAll() throws Exception {
|
||||
List<Episode> list = serienjunkies.getEpisodeList(new SerienjunkiesSearchResult(260, "Grey's Anatomy", null));
|
||||
|
||||
// check ordinary episode
|
||||
Episode eps = list.get(0);
|
||||
assertEquals("Grey's Anatomy", eps.getSeriesName());
|
||||
assertEquals("Nur 48 Stunden", eps.getTitle());
|
||||
assertEquals("1", eps.getEpisode().toString());
|
||||
assertEquals("1", eps.getSeason().toString());
|
||||
assertEquals("1", eps.getAbsolute().toString());
|
||||
|
||||
// check umlaut in title
|
||||
eps = list.get(2);
|
||||
assertEquals("Überleben ist alles", eps.getTitle());
|
||||
assertEquals("1", eps.getSeason().toString());
|
||||
assertEquals("3", eps.getEpisode().toString());
|
||||
assertEquals("3", eps.getAbsolute().toString());
|
||||
}
|
||||
|
||||
|
||||
@BeforeClass
|
||||
@AfterClass
|
||||
public static void clearCache() {
|
||||
CacheManager.getInstance().clearAll();
|
||||
}
|
||||
|
||||
}
|
|
@ -8,8 +8,8 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses( { TVDotComClientTest.class, AnidbClientTest.class, TVRageClientTest.class, TheTVDBClientTest.class, TMDbClientTest.class, IMDbClientTest.class, SubsceneSubtitleClientTest.class, SublightSubtitleClientTest.class,
|
||||
OpenSubtitlesXmlRpcTest.class })
|
||||
@SuiteClasses( { TVDotComClientTest.class, AnidbClientTest.class, TVRageClientTest.class, TheTVDBClientTest.class, SerienjunkiesClientTest.class, TMDbClientTest.class, IMDbClientTest.class, SubsceneSubtitleClientTest.class,
|
||||
SublightSubtitleClientTest.class, OpenSubtitlesXmlRpcTest.class })
|
||||
public class WebTestSuite {
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue