* optimize subtitle lookup

This commit is contained in:
Reinhard Pointner 2014-11-11 04:55:22 +00:00
parent a8b28ac8cd
commit 0ebf4b0a45
6 changed files with 50 additions and 39 deletions

View File

@ -7,6 +7,7 @@ import static net.filebot.media.MediaDetection.*;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -211,12 +212,17 @@ public final class WebServices {
} }
@Override @Override
public synchronized List<SearchResult> search(final String query) throws Exception { public synchronized List<SearchResult> search(final String query, final boolean byMovie, final boolean bySeries) throws Exception {
Callable<List<? extends SearchResult>> seriesSearch = () -> seriesIndex.search(query, Locale.ENGLISH); List<Callable<List<? extends SearchResult>>> queries = new ArrayList<>(2);
Callable<List<? extends SearchResult>> movieSearch = () -> movieIndex.searchMovie(query, Locale.ENGLISH); if (byMovie) {
queries.add(() -> movieIndex.searchMovie(query, Locale.ENGLISH));
}
if (bySeries) {
queries.add(() -> seriesIndex.search(query, Locale.ENGLISH));
}
Set<SearchResult> results = new LinkedHashSet<SearchResult>(); Set<SearchResult> results = new LinkedHashSet<SearchResult>();
for (Future<List<? extends SearchResult>> resultSet : requestThreadPool.invokeAll(asList(seriesSearch, movieSearch))) { for (Future<List<? extends SearchResult>> resultSet : requestThreadPool.invokeAll(queries)) {
try { try {
results.addAll(resultSet.get()); results.addAll(resultSet.get());
} catch (ExecutionException e) { } catch (ExecutionException e) {

View File

@ -65,14 +65,18 @@ public final class SubtitleUtilities {
throw new InterruptedException(); throw new InterruptedException();
// auto-detect query and search for subtitles // auto-detect query and search for subtitles
boolean searchByMovie = false, searchBySeries = false;
Collection<String> querySet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); Collection<String> querySet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
List<File> files = bySeries.getValue(); List<File> files = bySeries.getValue();
if (forceQuery != null && forceQuery.length() > 0) { if (forceQuery != null && forceQuery.length() > 0) {
querySet.add(forceQuery); querySet.add(forceQuery);
searchByMovie = true;
searchBySeries = true;
} else if (bySeries.getKey().length() > 0) { } else if (bySeries.getKey().length() > 0) {
// use auto-detected series name as query // use auto-detected series name as query
querySet.add(bySeries.getKey()); querySet.add(bySeries.getKey());
searchBySeries = true;
} else { } else {
for (File f : files) { for (File f : files) {
List<String> queries = new ArrayList<String>(); List<String> queries = new ArrayList<String>();
@ -88,11 +92,18 @@ public final class SubtitleUtilities {
queries.add(stripReleaseInfo(getName(f))); queries.add(stripReleaseInfo(getName(f)));
} }
querySet.addAll(queries); if (queries.size() > 0) {
querySet.addAll(queries);
searchByMovie = true;
}
} }
} }
Set<SubtitleDescriptor> subtitles = findSubtitles(service, querySet, languageName); if (!searchByMovie && !searchBySeries)
continue;
// search for subtitles online using the auto-detected or forced query information
Set<SubtitleDescriptor> subtitles = findSubtitles(service, querySet, searchByMovie, searchBySeries, languageName);
// allow early abort // allow early abort
if (Thread.interrupted()) if (Thread.interrupted())
@ -175,13 +186,13 @@ public final class SubtitleUtilities {
return subtitleByVideo; return subtitleByVideo;
} }
public static Set<SubtitleDescriptor> findSubtitles(SubtitleProvider service, Collection<String> querySet, String languageName) throws Exception { public static Set<SubtitleDescriptor> findSubtitles(SubtitleProvider service, Collection<String> querySet, boolean searchByMovie, boolean searchBySeries, String languageName) throws Exception {
Set<SubtitleDescriptor> subtitles = new LinkedHashSet<SubtitleDescriptor>(); Set<SubtitleDescriptor> subtitles = new LinkedHashSet<SubtitleDescriptor>();
// search for and automatically select movie / show entry // search for and automatically select movie / show entry
Set<SearchResult> resultSet = new HashSet<SearchResult>(); Set<SearchResult> resultSet = new HashSet<SearchResult>();
for (String query : querySet) { for (String query : querySet) {
resultSet.addAll(findProbableSearchResults(query, service.search(query), querySet.size() == 1 ? 4 : 2)); resultSet.addAll(findProbableSearchResults(query, service.search(query, searchByMovie, searchBySeries), querySet.size() == 1 ? 4 : 2));
} }
// fetch subtitles for all search results // fetch subtitles for all search results

View File

@ -51,6 +51,7 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.SwingWorker.StateValue; import javax.swing.SwingWorker.StateValue;
import javax.swing.border.Border; import javax.swing.border.Border;
@ -169,7 +170,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
protected void addSubtitleService(final SubtitleServiceBean service, final JPanel servicePanel) { protected void addSubtitleService(final SubtitleServiceBean service, final JPanel servicePanel) {
final LinkButton component = new LinkButton(service.getName(), null, ResourceManager.getIcon("database"), service.getLink()); final LinkButton component = new LinkButton(service.getDescription(), null, ResourceManager.getIcon("database"), service.getLink());
component.setBorder(BorderFactory.createEmptyBorder()); component.setBorder(BorderFactory.createEmptyBorder());
component.setVisible(false); component.setVisible(false);
@ -184,7 +185,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
component.setVisible(true); component.setVisible(true);
component.setToolTipText(service.getError() == null ? null : service.getError().getMessage()); component.setToolTipText(String.format("%s: %s", service.getName(), service.getError() == null ? service.getState().toString().toLowerCase() : service.getError().getMessage()));
servicePanel.setVisible(true); servicePanel.setVisible(true);
servicePanel.getParent().revalidate(); servicePanel.getParent().revalidate();
} }
@ -216,6 +217,11 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
} }
@Override
protected void done() {
SwingUtilities.invokeLater(() -> mappingModel.fireTableStructureChanged()); // make sure UI is refershed after completion
}
}; };
queryService = Executors.newFixedThreadPool(1); queryService = Executors.newFixedThreadPool(1);
@ -842,7 +848,7 @@ class SubtitleAutoMatchDialog extends JDialog {
private final URI link; private final URI link;
private StateValue state = StateValue.PENDING; private StateValue state = StateValue.PENDING;
private Throwable error = null; private Exception error = null;
public SubtitleServiceBean(String name, Icon icon, URI link) { public SubtitleServiceBean(String name, Icon icon, URI link) {
this.name = name; this.name = name;
@ -862,6 +868,8 @@ class SubtitleAutoMatchDialog extends JDialog {
return link; return link;
} }
public abstract String getDescription();
public abstract float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor); public abstract float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor);
protected abstract Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception; protected abstract Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception;
@ -872,11 +880,7 @@ class SubtitleAutoMatchDialog extends JDialog {
try { try {
return getSubtitleList(files, languageName, parent); return getSubtitleList(files, languageName, parent);
} catch (Exception e) { } catch (Exception e) {
// remember error throw (error = e);
error = e;
// rethrow error
throw e;
} finally { } finally {
setState(StateValue.DONE); setState(StateValue.DONE);
} }
@ -906,8 +910,8 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
@Override @Override
public String getName() { public String getDescription() {
return String.format("%s [via hash]", service.getName()); return "Exact Search";
} }
@Override @Override
@ -923,18 +927,16 @@ class SubtitleAutoMatchDialog extends JDialog {
protected static class SubtitleProviderBean extends SubtitleServiceBean { protected static class SubtitleProviderBean extends SubtitleServiceBean {
private SubtitleAutoMatchDialog inputProvider;
private SubtitleProvider service; private SubtitleProvider service;
public SubtitleProviderBean(SubtitleProvider service, SubtitleAutoMatchDialog inputProvider) { public SubtitleProviderBean(SubtitleProvider service, SubtitleAutoMatchDialog inputProvider) {
super(service.getName(), service.getIcon(), service.getLink()); super(service.getName(), service.getIcon(), service.getLink());
this.service = service; this.service = service;
this.inputProvider = inputProvider;
} }
@Override @Override
public String getName() { public String getDescription() {
return String.format("%s [via name]", service.getName()); return "Fuzzy Search";
} }
@Override @Override

View File

@ -214,7 +214,7 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
@Override @Override
public Collection<SearchResult> search() throws Exception { public Collection<SearchResult> search() throws Exception {
return request.getProvider().search(request.getSearchText()); return request.getProvider().search(request.getSearchText(), true, true);
} }
@Override @Override

View File

@ -90,8 +90,8 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
} }
@Override @Override
public synchronized List<SearchResult> search(String query) throws Exception { public synchronized List<SearchResult> search(String query, boolean byMovie, boolean bySeries) throws Exception {
throw new UnsupportedOperationException("SearchMoviesOnIMDB is not allowed due to abuse"); throw new UnsupportedOperationException(); // XMLRPC::SearchMoviesOnIMDB is not allowed due to abuse
} }
@Override @Override
@ -499,7 +499,7 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
List<String> languages = xmlrpc.detectLanguage(data); List<String> languages = xmlrpc.detectLanguage(data);
// return first language // return first language
language = languages.size() > 0 ? languages.get(0) : ""; language = languages.size() > 0 ? languages.get(0) : "";
getCache().putData("detectLanguage", md5(data), Locale.ROOT, language); getCache().putData("detectLanguage", md5(data), Locale.ROOT, language);
return new Locale(language); return new Locale(language);
} }

View File

@ -1,30 +1,22 @@
package net.filebot.web; package net.filebot.web;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
public interface SubtitleProvider { public interface SubtitleProvider {
public List<SearchResult> search(String query) throws Exception; public List<SearchResult> search(String query, boolean byMovie, boolean bySeries) throws Exception;
public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, String languageName) throws Exception; public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, String languageName) throws Exception;
public URI getSubtitleListLink(SearchResult searchResult, String languageName); public URI getSubtitleListLink(SearchResult searchResult, String languageName);
public String getName(); public String getName();
public URI getLink(); public URI getLink();
public Icon getIcon(); public Icon getIcon();
} }