2014-04-19 02:30:29 -04:00
|
|
|
package net.filebot;
|
2010-05-13 10:20:33 -04:00
|
|
|
|
2013-09-11 13:22:00 -04:00
|
|
|
import static java.util.Arrays.*;
|
|
|
|
import static java.util.Collections.*;
|
2014-04-19 02:30:29 -04:00
|
|
|
import static net.filebot.Settings.*;
|
|
|
|
import static net.filebot.media.MediaDetection.*;
|
2010-05-13 10:20:33 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
import java.io.IOException;
|
2014-10-29 08:55:27 -04:00
|
|
|
import java.net.URI;
|
2012-10-14 07:57:25 -04:00
|
|
|
import java.util.LinkedHashSet;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.concurrent.Callable;
|
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.Future;
|
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
2014-07-17 03:08:23 -04:00
|
|
|
import net.filebot.media.XattrMetaInfoProvider;
|
2014-10-29 08:55:27 -04:00
|
|
|
import net.filebot.similarity.MetricAvg;
|
2014-04-19 02:30:29 -04:00
|
|
|
import net.filebot.web.AcoustIDClient;
|
|
|
|
import net.filebot.web.AnidbClient;
|
|
|
|
import net.filebot.web.AnidbSearchResult;
|
|
|
|
import net.filebot.web.AudioTrack;
|
|
|
|
import net.filebot.web.EpisodeListProvider;
|
|
|
|
import net.filebot.web.FanartTVClient;
|
|
|
|
import net.filebot.web.ID3Lookup;
|
|
|
|
import net.filebot.web.LocalSearch;
|
|
|
|
import net.filebot.web.Movie;
|
|
|
|
import net.filebot.web.MovieIdentificationService;
|
|
|
|
import net.filebot.web.MusicIdentificationService;
|
2014-08-05 04:23:39 -04:00
|
|
|
import net.filebot.web.OMDbClient;
|
2014-04-19 02:30:29 -04:00
|
|
|
import net.filebot.web.OpenSubtitlesClient;
|
|
|
|
import net.filebot.web.SearchResult;
|
2014-10-29 08:55:27 -04:00
|
|
|
import net.filebot.web.SubtitleDescriptor;
|
2014-04-19 02:30:29 -04:00
|
|
|
import net.filebot.web.SubtitleProvider;
|
|
|
|
import net.filebot.web.TMDbClient;
|
|
|
|
import net.filebot.web.TVRageClient;
|
|
|
|
import net.filebot.web.TVRageSearchResult;
|
|
|
|
import net.filebot.web.TheTVDBClient;
|
2014-10-29 08:55:27 -04:00
|
|
|
import net.filebot.web.TheTVDBClient.SeriesInfo;
|
2014-04-19 02:30:29 -04:00
|
|
|
import net.filebot.web.TheTVDBSearchResult;
|
|
|
|
import net.filebot.web.VideoHashSubtitleService;
|
2010-05-13 10:20:33 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reuse the same web service client so login, cache, etc. can be shared.
|
|
|
|
*/
|
|
|
|
public final class WebServices {
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2010-05-13 10:20:33 -04:00
|
|
|
// episode dbs
|
2014-08-07 05:35:19 -04:00
|
|
|
public static final TVRageClient TVRage = new TVRageClient(getApiKey("tvrage"));
|
2014-08-08 12:45:23 -04:00
|
|
|
public static final AnidbClient AniDB = new AnidbClientWithLocalSearch(getApiKey("anidb"), 6);
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
// extended TheTVDB module with local search
|
2014-08-07 05:35:19 -04:00
|
|
|
public static final TheTVDBClientWithLocalSearch TheTVDB = new TheTVDBClientWithLocalSearch(getApiKey("thetvdb"));
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-07-15 22:42:15 -04:00
|
|
|
// movie dbs
|
2014-08-05 04:23:39 -04:00
|
|
|
public static final OMDbClient OMDb = new OMDbClient();
|
2014-08-07 05:35:19 -04:00
|
|
|
public static final TMDbClient TheMovieDB = new TMDbClient(getApiKey("themoviedb"));
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2010-05-13 10:20:33 -04:00
|
|
|
// subtitle dbs
|
2014-10-29 08:55:27 -04:00
|
|
|
public static final OpenSubtitlesClient OpenSubtitles = new OpenSubtitlesClientWithLocalSearch(getApiKey("opensubtitles"), getApplicationVersion(), TheTVDB, TheMovieDB);
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-01-10 11:38:17 -05:00
|
|
|
// misc
|
2014-08-07 05:35:19 -04:00
|
|
|
public static final FanartTVClient FanartTV = new FanartTVClient(Settings.getApiKey("fanart.tv"));
|
|
|
|
public static final AcoustIDClient AcoustID = new AcoustIDClient(Settings.getApiKey("acoustid"));
|
2014-07-17 03:08:23 -04:00
|
|
|
public static final XattrMetaInfoProvider XattrMetaData = new XattrMetaInfoProvider();
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2010-05-13 10:20:33 -04:00
|
|
|
public static EpisodeListProvider[] getEpisodeListProviders() {
|
2014-08-14 02:36:49 -04:00
|
|
|
return new EpisodeListProvider[] { TheTVDB, AniDB, TVRage };
|
2010-05-13 10:20:33 -04:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2011-09-13 14:16:38 -04:00
|
|
|
public static MovieIdentificationService[] getMovieIdentificationServices() {
|
2014-08-05 04:23:39 -04:00
|
|
|
return new MovieIdentificationService[] { TheMovieDB, OMDb };
|
2011-09-13 14:16:38 -04:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2010-05-13 10:20:33 -04:00
|
|
|
public static SubtitleProvider[] getSubtitleProviders() {
|
2013-06-24 05:25:04 -04:00
|
|
|
return new SubtitleProvider[] { OpenSubtitles };
|
2010-05-13 10:20:33 -04:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2010-05-13 10:20:33 -04:00
|
|
|
public static VideoHashSubtitleService[] getVideoHashSubtitleServices() {
|
2013-06-24 05:25:04 -04:00
|
|
|
return new VideoHashSubtitleService[] { OpenSubtitles };
|
2010-05-13 10:20:33 -04:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-01-12 10:21:33 -05:00
|
|
|
public static MusicIdentificationService[] getMusicIdentificationServices() {
|
|
|
|
return new MusicIdentificationService[] { AcoustID, new ID3Lookup() };
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2011-09-13 14:16:38 -04:00
|
|
|
public static EpisodeListProvider getEpisodeListProvider(String name) {
|
|
|
|
for (EpisodeListProvider it : WebServices.getEpisodeListProviders()) {
|
|
|
|
if (it.getName().equalsIgnoreCase(name))
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
return null; // default
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2011-09-13 14:16:38 -04:00
|
|
|
public static MovieIdentificationService getMovieIdentificationService(String name) {
|
|
|
|
for (MovieIdentificationService it : getMovieIdentificationServices()) {
|
|
|
|
if (it.getName().equalsIgnoreCase(name))
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
return null; // default
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-01-12 10:21:33 -05:00
|
|
|
public static MusicIdentificationService getMusicIdentificationService(String name) {
|
|
|
|
for (MusicIdentificationService it : getMusicIdentificationServices()) {
|
|
|
|
if (it.getName().equalsIgnoreCase(name))
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
return null; // default
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2014-01-04 15:18:47 -05:00
|
|
|
public static Object getServiceBySearchResult(Object r) {
|
|
|
|
if (r instanceof TheTVDBSearchResult)
|
|
|
|
return WebServices.TheTVDB;
|
|
|
|
if (r instanceof AnidbSearchResult)
|
|
|
|
return WebServices.AniDB;
|
|
|
|
if (r instanceof TVRageSearchResult)
|
|
|
|
return WebServices.TVRage;
|
|
|
|
if (r instanceof Movie)
|
2014-04-13 15:39:46 -04:00
|
|
|
return WebServices.TheMovieDB;
|
2014-01-04 15:18:47 -05:00
|
|
|
if (r instanceof AudioTrack)
|
|
|
|
return WebServices.AcoustID;
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2013-02-07 03:34:08 -05:00
|
|
|
public static class TheTVDBClientWithLocalSearch extends TheTVDBClient {
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
public TheTVDBClientWithLocalSearch(String apikey) {
|
|
|
|
super(apikey);
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
// index of local thetvdb data dump
|
|
|
|
private static LocalSearch<SearchResult> localIndex;
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-02-07 03:34:08 -05:00
|
|
|
public synchronized LocalSearch<SearchResult> getLocalIndex() throws IOException {
|
2012-10-14 07:57:25 -04:00
|
|
|
if (localIndex == null) {
|
|
|
|
// fetch data dump
|
2013-10-13 10:50:45 -04:00
|
|
|
TheTVDBSearchResult[] data = releaseInfo.getTheTVDBIndex();
|
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
// index data dump
|
|
|
|
localIndex = new LocalSearch<SearchResult>(asList(data)) {
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
@Override
|
|
|
|
protected Set<String> getFields(SearchResult object) {
|
2013-10-13 10:50:45 -04:00
|
|
|
return set(object.getEffectiveNames());
|
2012-10-14 07:57:25 -04:00
|
|
|
}
|
|
|
|
};
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 08:13:45 -04:00
|
|
|
// make local search more restrictive
|
|
|
|
localIndex.setResultMinimumSimilarity(0.7f);
|
2012-10-14 07:57:25 -04:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
return localIndex;
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-04-07 01:57:01 -04:00
|
|
|
public SeriesInfo getSeriesInfoByLocalIndex(String name, Locale locale) throws Exception {
|
2013-05-21 13:17:19 -04:00
|
|
|
List<SearchResult> results = getLocalIndex().search(name);
|
|
|
|
if (results.size() > 0) {
|
|
|
|
return getSeriesInfo((TheTVDBSearchResult) results.get(0), locale);
|
2013-04-07 01:57:01 -04:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
@Override
|
|
|
|
public List<SearchResult> fetchSearchResult(final String query, final Locale locale) throws Exception {
|
|
|
|
Callable<List<SearchResult>> apiSearch = new Callable<List<SearchResult>>() {
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
@Override
|
|
|
|
public List<SearchResult> call() throws Exception {
|
|
|
|
return TheTVDBClientWithLocalSearch.super.fetchSearchResult(query, locale);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Callable<List<SearchResult>> localSearch = new Callable<List<SearchResult>>() {
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
@Override
|
|
|
|
public List<SearchResult> call() throws Exception {
|
|
|
|
try {
|
|
|
|
return getLocalIndex().search(query);
|
|
|
|
} catch (Exception e) {
|
|
|
|
Logger.getLogger(TheTVDBClientWithLocalSearch.class.getName()).log(Level.SEVERE, e.getMessage(), e);
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
// let local search fail gracefully without affecting API search
|
|
|
|
return emptyList();
|
|
|
|
}
|
|
|
|
};
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-10-14 07:57:25 -04:00
|
|
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
|
|
|
try {
|
|
|
|
Set<SearchResult> results = new LinkedHashSet<SearchResult>();
|
2013-10-13 10:50:45 -04:00
|
|
|
for (Future<List<SearchResult>> resultSet : executor.invokeAll(asList(localSearch, apiSearch))) {
|
2012-10-14 07:57:25 -04:00
|
|
|
try {
|
|
|
|
results.addAll(resultSet.get());
|
|
|
|
} catch (ExecutionException e) {
|
|
|
|
if (e.getCause() instanceof Exception) {
|
|
|
|
throw (Exception) e.getCause(); // unwrap cause
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
return sortBySimilarity(results, singleton(query), getSeriesMatchMetric(), false);
|
2012-10-14 07:57:25 -04:00
|
|
|
} finally {
|
|
|
|
executor.shutdownNow();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-03-17 10:19:11 -04:00
|
|
|
public static class AnidbClientWithLocalSearch extends AnidbClient {
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-03-17 10:19:11 -04:00
|
|
|
public AnidbClientWithLocalSearch(String client, int clientver) {
|
|
|
|
super(client, clientver);
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-03-17 10:19:11 -04:00
|
|
|
@Override
|
|
|
|
public List<AnidbSearchResult> getAnimeTitles() throws Exception {
|
2013-10-13 10:50:45 -04:00
|
|
|
return asList(releaseInfo.getAnidbIndex());
|
2013-03-17 10:19:11 -04:00
|
|
|
}
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2014-10-29 08:55:27 -04:00
|
|
|
public static class OpenSubtitlesClientWithLocalSearch extends OpenSubtitlesClient {
|
|
|
|
|
|
|
|
private final EpisodeListProvider seriesIndex;
|
|
|
|
private final MovieIdentificationService movieIndex;
|
|
|
|
|
|
|
|
public OpenSubtitlesClientWithLocalSearch(String name, String version, EpisodeListProvider seriesIndex, MovieIdentificationService movieIndex) {
|
|
|
|
super(name, version);
|
|
|
|
this.seriesIndex = seriesIndex;
|
|
|
|
this.movieIndex = movieIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized List<SearchResult> search(final String query) throws Exception {
|
|
|
|
Callable<List<? extends SearchResult>> seriesSearch = () -> seriesIndex.search(query, Locale.ENGLISH);
|
|
|
|
Callable<List<? extends SearchResult>> movieSearch = () -> movieIndex.searchMovie(query, Locale.ENGLISH);
|
|
|
|
|
|
|
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
|
|
|
try {
|
|
|
|
Set<SearchResult> results = new LinkedHashSet<SearchResult>();
|
|
|
|
for (Future<List<? extends SearchResult>> resultSet : executor.invokeAll(asList(seriesSearch, movieSearch))) {
|
|
|
|
try {
|
|
|
|
results.addAll(resultSet.get());
|
|
|
|
} catch (ExecutionException e) {
|
|
|
|
if (e.getCause() instanceof Exception) {
|
|
|
|
throw (Exception) e.getCause(); // unwrap cause
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sortBySimilarity(results, singleton(query), new MetricAvg(getSeriesMatchMetric(), getMovieMatchMetric()), false);
|
|
|
|
} finally {
|
|
|
|
executor.shutdownNow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, String languageName) throws Exception {
|
|
|
|
return super.getSubtitleList(getIMDbID(searchResult), languageName);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public URI getSubtitleListLink(SearchResult searchResult, String languageName) {
|
|
|
|
try {
|
|
|
|
return super.getSubtitleListLink(getIMDbID(searchResult), languageName);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public Movie getIMDbID(SearchResult result) throws Exception {
|
|
|
|
if (result instanceof TheTVDBSearchResult) {
|
|
|
|
TheTVDBSearchResult s = (TheTVDBSearchResult) result;
|
|
|
|
SeriesInfo seriesInfo = ((TheTVDBClient) seriesIndex).getSeriesInfo(s, Locale.ENGLISH);
|
|
|
|
if (seriesInfo.getImdbId() != null) {
|
|
|
|
return new Movie(seriesInfo.getName(), seriesInfo.getFirstAired().getYear(), seriesInfo.getImdbId(), -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (result instanceof Movie) {
|
|
|
|
Movie m = (Movie) result;
|
|
|
|
return m.getImdbId() > 0 ? m : movieIndex.getMovieDescriptor(m, Locale.ENGLISH);
|
|
|
|
}
|
|
|
|
throw new IllegalArgumentException(String.format("'%s' not found", result));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-13 10:20:33 -04:00
|
|
|
/**
|
|
|
|
* Dummy constructor to prevent instantiation.
|
|
|
|
*/
|
|
|
|
private WebServices() {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2012-07-16 05:58:18 -04:00
|
|
|
/**
|
|
|
|
* Initialize client settings from system properties
|
|
|
|
*/
|
|
|
|
static {
|
2012-07-23 02:58:36 -04:00
|
|
|
String[] osdbLogin = getLogin("osdb.user");
|
2012-07-16 05:58:18 -04:00
|
|
|
OpenSubtitles.setUser(osdbLogin[0], osdbLogin[1]);
|
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-02-26 01:21:02 -05:00
|
|
|
public static String[] getLogin(String key) {
|
2014-08-09 01:57:54 -04:00
|
|
|
try {
|
|
|
|
String[] values = Settings.forPackage(WebServices.class).get(key, ":").split(":", 2); // empty username/password by default
|
|
|
|
if (values != null && values.length == 2 && values[0] != null && values[1] != null) {
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
Logger.getLogger(WebServices.class.getName()).log(Level.WARNING, e.getMessage(), e);
|
|
|
|
}
|
|
|
|
return new String[] { "", "" };
|
2013-02-26 01:21:02 -05:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2013-02-26 01:21:02 -05:00
|
|
|
public static void setLogin(String id, String user, String password) {
|
2014-08-09 01:57:54 -04:00
|
|
|
if (user == null || password == null || user.contains(":") || (user.isEmpty() && !password.isEmpty()) || (!user.isEmpty() && password.isEmpty())) {
|
|
|
|
throw new IllegalArgumentException("Illegal login: " + user);
|
2012-07-23 02:58:36 -04:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2014-08-09 01:57:54 -04:00
|
|
|
Settings settings = Settings.forPackage(WebServices.class);
|
|
|
|
String value = user.isEmpty() && password.isEmpty() ? null : user + ":" + password;
|
|
|
|
|
2013-02-26 01:21:02 -05:00
|
|
|
if (id.equals("osdb.user")) {
|
|
|
|
settings.put(id, value);
|
|
|
|
OpenSubtitles.setUser(user, password);
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
2012-07-16 05:58:18 -04:00
|
|
|
}
|
2013-10-13 10:50:45 -04:00
|
|
|
|
2010-05-13 10:20:33 -04:00
|
|
|
}
|