+ OpenSubtitles look-up optimizations
This commit is contained in:
parent
6ec79ba149
commit
5bf64d6ab1
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue