+ OpenSubtitles look-up optimizations

This commit is contained in:
Reinhard Pointner 2014-10-29 12:55:27 +00:00
parent 6ec79ba149
commit 5bf64d6ab1
6 changed files with 126 additions and 20 deletions

View File

@ -6,6 +6,7 @@ import static net.filebot.Settings.*;
import static net.filebot.media.MediaDetection.*;
import java.io.IOException;
import java.net.URI;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
@ -19,6 +20,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.filebot.media.XattrMetaInfoProvider;
import net.filebot.similarity.MetricAvg;
import net.filebot.web.AcoustIDClient;
import net.filebot.web.AnidbClient;
import net.filebot.web.AnidbSearchResult;
@ -33,11 +35,13 @@ import net.filebot.web.MusicIdentificationService;
import net.filebot.web.OMDbClient;
import net.filebot.web.OpenSubtitlesClient;
import net.filebot.web.SearchResult;
import net.filebot.web.SubtitleDescriptor;
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;
import net.filebot.web.TheTVDBClient.SeriesInfo;
import net.filebot.web.TheTVDBSearchResult;
import net.filebot.web.VideoHashSubtitleService;
@ -58,7 +62,7 @@ public final class WebServices {
public static final TMDbClient TheMovieDB = new TMDbClient(getApiKey("themoviedb"));
// subtitle dbs
public static final OpenSubtitlesClient OpenSubtitles = new OpenSubtitlesClient(String.format("%s v%s", getApiKey("opensubtitles"), getApplicationVersion()));
public static final OpenSubtitlesClient OpenSubtitles = new OpenSubtitlesClientWithLocalSearch(getApiKey("opensubtitles"), getApplicationVersion(), TheTVDB, TheMovieDB);
// misc
public static final FanartTVClient FanartTV = new FanartTVClient(Settings.getApiKey("fanart.tv"));
@ -218,6 +222,70 @@ public final class WebServices {
}
}
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));
}
}
/**
* Dummy constructor to prevent instantiation.
*/

View File

@ -181,7 +181,7 @@ public final class SubtitleUtilities {
// search for and automatically select movie / show entry
Set<SearchResult> resultSet = new HashSet<SearchResult>();
for (String query : querySet) {
resultSet.addAll(findProbableSearchResults(query, service.search(query)));
resultSet.addAll(findProbableSearchResults(query, service.search(query), querySet.size() == 1 ? 4 : 2));
}
// fetch subtitles for all search results
@ -192,7 +192,7 @@ public final class SubtitleUtilities {
return subtitles;
}
protected static Collection<SearchResult> findProbableSearchResults(String query, Iterable<? extends SearchResult> searchResults) {
protected static Collection<SearchResult> findProbableSearchResults(String query, Iterable<? extends SearchResult> searchResults, int limit) {
// auto-select most probable search result
Set<SearchResult> probableMatches = new LinkedHashSet<SearchResult>();
@ -201,8 +201,10 @@ public final class SubtitleUtilities {
// find probable matches using name similarity > threshold
for (SearchResult result : searchResults) {
if (metric.getSimilarity(query, removeTrailingBrackets(result.getName())) > 0.8f || result.getName().toLowerCase().startsWith(query.toLowerCase())) {
probableMatches.add(result);
if (probableMatches.size() <= limit) {
if (metric.getSimilarity(query, removeTrailingBrackets(result.getName())) > 0.8f || result.getName().toLowerCase().startsWith(query.toLowerCase())) {
probableMatches.add(result);
}
}
}

View File

@ -304,7 +304,7 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
try {
if (osdbUser.getText().length() > 0 && osdbPass.getPassword().length > 0) {
OpenSubtitlesClient osdb = new OpenSubtitlesClient(String.format("%s v%s", getApplicationName(), getApplicationVersion()));
OpenSubtitlesClient osdb = new OpenSubtitlesClient(getApplicationName(), getApplicationVersion());
osdb.setUser(osdbUser.getText(), new String(osdbPass.getPassword()));
osdb.login();
}

View File

@ -164,7 +164,7 @@ public class SubtitleUploadDialog extends JDialog {
File video = mapping.getVideo() != null ? mapping.getVideo() : mapping.getSubtitle();
String input = showInputDialog("Enter movie / series name:", stripReleaseInfo(FileUtilities.getName(video)), String.format("%s/%s", video.getParentFile().getName(), video.getName()), SubtitleUploadDialog.this);
if (input != null && input.length() > 0) {
List<Movie> options = database.searchMovie(input, Locale.ENGLISH);
List<Movie> options = database.searchIMDB(input);
if (options.size() > 0) {
SelectDialog<Movie> dialog = new SelectDialog<Movie>(SubtitleUploadDialog.this, options);
dialog.setLocation(getOffsetLocation(dialog.getOwner()));

View File

@ -54,8 +54,8 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
private String username = "";
private String password = "";
public OpenSubtitlesClient(String useragent) {
this.xmlrpc = new OpenSubtitlesXmlRpcWithRetryAndFloodLimit(useragent, 2, 3000);
public OpenSubtitlesClient(String name, String version) {
this.xmlrpc = new OpenSubtitlesXmlRpcWithRetryAndFloodLimit(String.format("%s v%s", name, version), 2, 3000);
}
public synchronized void setUser(String username, String password) {
@ -90,12 +90,12 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
}
@Override
public List<SearchResult> search(String query) throws Exception {
throw new UnsupportedOperationException("SearchMoviesOnIMDB is not supported due to abuse");
public synchronized List<SearchResult> search(String query) throws Exception {
throw new UnsupportedOperationException("SearchMoviesOnIMDB is not allowed due to abuse");
}
@Override
public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, String languageName) throws Exception {
public synchronized List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, String languageName) throws Exception {
List<SubtitleDescriptor> subtitles = getCache().getSubtitleDescriptorList(searchResult, languageName);
if (subtitles != null) {
return subtitles;
@ -142,7 +142,7 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
// max numbers of queries to submit in a single XML-RPC request, but currently only batchSize == 1 is supported
private final int batchSize = 1;
public Map<File, List<SubtitleDescriptor>> getSubtitleListByHash(File[] files, String languageName) throws Exception {
public synchronized Map<File, List<SubtitleDescriptor>> getSubtitleListByHash(File[] files, String languageName) throws Exception {
// singleton array with or empty array
String[] languageFilter = languageName != null ? new String[] { getSubLanguageID(languageName) } : new String[0];
@ -205,7 +205,7 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
return resultMap;
}
public Map<File, List<SubtitleDescriptor>> getSubtitleListByTag(File[] files, String languageName) throws Exception {
public synchronized Map<File, List<SubtitleDescriptor>> getSubtitleListByTag(File[] files, String languageName) throws Exception {
// singleton array with or empty array
String[] languageFilter = languageName != null ? new String[] { getSubLanguageID(languageName) } : new String[0];
@ -362,12 +362,50 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
@Override
public List<Movie> searchMovie(String query, Locale locale) throws Exception {
throw new UnsupportedOperationException("SearchMoviesOnIMDB is not supported due to abuse");
throw new UnsupportedOperationException();
}
public synchronized List<Movie> searchIMDB(String query) throws Exception {
// search for movies and series
List<SearchResult> result = getCache().getSearchResult("search", query, null);
if (result != null) {
return (List) result;
}
// require login
login();
try {
// search for movies / series
List<Movie> resultSet = xmlrpc.searchMoviesOnIMDB(query);
result = asList(resultSet.toArray(new SearchResult[0]));
} catch (ClassCastException e) {
// unexpected xmlrpc responses (e.g. error messages instead of results) will trigger this
throw new XmlRpcException("Illegal XMLRPC response on searchMoviesOnIMDB");
}
getCache().putSearchResult("search", query, null, result);
return (List) result;
}
@Override
public Movie getMovieDescriptor(Movie id, Locale locale) throws Exception {
throw new UnsupportedOperationException("GetIMDBMovieDetails is not supported due to abuse");
public synchronized Movie getMovieDescriptor(Movie id, Locale locale) throws Exception {
if (id.getImdbId() <= 0) {
throw new IllegalArgumentException("id must not be " + id.getImdbId());
}
Movie result = getCache().getData("getMovieDescriptor", id.getImdbId(), locale, Movie.class);
if (result != null) {
return result;
}
// require login
login();
Movie movie = xmlrpc.getIMDBMovieDetails(id.getImdbId());
getCache().putData("getMovieDescriptor", id.getImdbId(), locale, movie);
return movie;
}
public Movie getMovieDescriptor(File movieFile, Locale locale) throws Exception {
@ -375,7 +413,7 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
}
@Override
public Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception {
public synchronized Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception {
// create result array
Map<File, Movie> result = new HashMap<File, Movie>();

View File

@ -112,7 +112,6 @@ public class OpenSubtitlesXmlRpc {
}
@SuppressWarnings("unchecked")
@Deprecated
public List<Movie> searchMoviesOnIMDB(String query) throws XmlRpcFault {
Map<?, ?> response = invoke("SearchMoviesOnIMDB", token, query);
@ -146,7 +145,6 @@ public class OpenSubtitlesXmlRpc {
}
@SuppressWarnings("unchecked")
@Deprecated
public Movie getIMDBMovieDetails(int imdbid) throws XmlRpcFault {
Map<?, ?> response = invoke("GetIMDBMovieDetails", token, imdbid);