* allow manual input if no subtitles have been found via auto-matching
* allow multi-value input in input dialog * make sure background workers get cancelled / terminated as expected
This commit is contained in:
parent
c6bfd7755f
commit
373b0c2662
|
@ -51,7 +51,7 @@ public final class WebServices {
|
|||
|
||||
|
||||
public static SubtitleProvider[] getSubtitleProviders() {
|
||||
return new SubtitleProvider[] { OpenSubtitles, Subscene, Sublight };
|
||||
return new SubtitleProvider[] { OpenSubtitles, Sublight, Subscene };
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -290,6 +290,13 @@ public class SeriesNameMatcher {
|
|||
|
||||
@Override
|
||||
public boolean add(String value) {
|
||||
value = value.trim();
|
||||
|
||||
// require series name to have at least two characters
|
||||
if (value.length() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String current = data.get(key(value));
|
||||
|
||||
// prefer strings with similar upper/lower case ratio (e.g. prefer Roswell over roswell)
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
package net.sourceforge.filebot.ui;
|
||||
|
||||
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
@ -37,8 +38,8 @@ public class SelectDialog<T> extends JDialog {
|
|||
private boolean valueSelected = false;
|
||||
|
||||
|
||||
public SelectDialog(Window owner, Collection<? extends T> options) {
|
||||
super(owner, "Select", ModalityType.DOCUMENT_MODAL);
|
||||
public SelectDialog(Component parent, Collection<? extends T> options) {
|
||||
super(getWindow(parent), "Select", ModalityType.DOCUMENT_MODAL);
|
||||
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.rename;
|
||||
|
||||
|
||||
import java.awt.Window;
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -12,5 +12,5 @@ import net.sourceforge.filebot.similarity.Match;
|
|||
|
||||
interface AutoCompleteMatcher {
|
||||
|
||||
List<Match<File, ?>> match(List<File> files, Locale locale, boolean autodetection, Window parent) throws Exception;
|
||||
List<Match<File, ?>> match(List<File> files, Locale locale, boolean autodetection, Component parent) throws Exception;
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ package net.sourceforge.filebot.ui.rename;
|
|||
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.filebot.similarity.SeriesNameMatcher.*;
|
||||
import static net.sourceforge.filebot.web.EpisodeUtilities.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Window;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -56,7 +57,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
}
|
||||
|
||||
|
||||
protected SearchResult selectSearchResult(final String query, final List<SearchResult> searchResults, final Window window) throws Exception {
|
||||
protected SearchResult selectSearchResult(final String query, final List<SearchResult> searchResults, final Component parent) throws Exception {
|
||||
if (searchResults.size() == 1) {
|
||||
return searchResults.get(0);
|
||||
}
|
||||
|
@ -67,9 +68,9 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
// use name similarity metric
|
||||
SimilarityMetric metric = new NameSimilarityMetric();
|
||||
|
||||
// find probable matches using name similarity > 0.9
|
||||
// find probable matches using name similarity >= 0.9
|
||||
for (SearchResult result : searchResults) {
|
||||
if (metric.getSimilarity(normalizeName(query), normalizeName(result.getName())) > 0.9) {
|
||||
if (metric.getSimilarity(normalizeName(query), normalizeName(result.getName())) >= 0.9) {
|
||||
probableMatches.add(result);
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +86,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
@Override
|
||||
public SearchResult call() throws Exception {
|
||||
// multiple results have been found, user must select one
|
||||
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(window, searchResults);
|
||||
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(parent, searchResults);
|
||||
|
||||
selectDialog.getHeaderLabel().setText(String.format("Shows matching '%s':", query));
|
||||
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore");
|
||||
|
@ -117,12 +118,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
}
|
||||
|
||||
|
||||
protected Collection<String> detectSeriesNames(Collection<File> files) {
|
||||
return new SeriesNameMatcher().matchAll(files.toArray(new File[0]));
|
||||
}
|
||||
|
||||
|
||||
protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, final Locale locale, final Window window) throws Exception {
|
||||
protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, final Locale locale, final Component parent) throws Exception {
|
||||
List<Callable<List<Episode>>> tasks = new ArrayList<Callable<List<Episode>>>();
|
||||
|
||||
// detect series names and create episode list fetch tasks
|
||||
|
@ -135,7 +131,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
|
||||
// select search result
|
||||
if (results.size() > 0) {
|
||||
SearchResult selectedSearchResult = selectSearchResult(query, results, window);
|
||||
SearchResult selectedSearchResult = selectSearchResult(query, results, parent);
|
||||
|
||||
if (selectedSearchResult != null) {
|
||||
List<Episode> episodes = provider.getEpisodeList(selectedSearchResult, locale);
|
||||
|
@ -171,14 +167,14 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
|
||||
|
||||
@Override
|
||||
public List<Match<File, ?>> match(final List<File> files, final Locale locale, final boolean autodetection, final Window window) throws Exception {
|
||||
public List<Match<File, ?>> match(final List<File> files, final Locale locale, final boolean autodetection, final Component parent) throws Exception {
|
||||
// focus on movie and subtitle files
|
||||
final List<File> mediaFiles = FileUtilities.filter(files, VIDEO_FILES, SUBTITLE_FILES);
|
||||
final Map<File, List<File>> filesByFolder = mapByFolder(mediaFiles);
|
||||
|
||||
// do matching all at once
|
||||
if (filesByFolder.keySet().size() <= 5 || detectSeriesNames(mediaFiles).size() <= 5) {
|
||||
return matchEpisodeSet(mediaFiles, locale, autodetection, window);
|
||||
if (filesByFolder.keySet().size() <= 5 || detectSeriesName(mediaFiles).size() <= 5) {
|
||||
return matchEpisodeSet(mediaFiles, locale, autodetection, parent);
|
||||
}
|
||||
|
||||
// assume that many shows will be matched, do it folder by folder
|
||||
|
@ -190,7 +186,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
|
||||
@Override
|
||||
public List<Match<File, ?>> call() throws Exception {
|
||||
return matchEpisodeSet(folder, locale, autodetection, window);
|
||||
return matchEpisodeSet(folder, locale, autodetection, parent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -214,16 +210,16 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
}
|
||||
|
||||
|
||||
public List<Match<File, ?>> matchEpisodeSet(final List<File> files, Locale locale, boolean autodetection, Window window) throws Exception {
|
||||
public List<Match<File, ?>> matchEpisodeSet(final List<File> files, Locale locale, boolean autodetection, Component parent) throws Exception {
|
||||
Set<Episode> episodes = emptySet();
|
||||
|
||||
// detect series name and fetch episode list
|
||||
if (autodetection) {
|
||||
Collection<String> names = detectSeriesNames(files);
|
||||
Collection<String> names = detectSeriesName(files);
|
||||
if (names.size() > 0) {
|
||||
// only allow one fetch session at a time so later requests can make use of cached results
|
||||
synchronized (provider) {
|
||||
episodes = fetchEpisodeSet(names, locale, window);
|
||||
episodes = fetchEpisodeSet(names, locale, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,15 +235,15 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
suggestion = files.get(0).getParentFile().getName();
|
||||
}
|
||||
|
||||
String input = null;
|
||||
List<String> input = emptyList();
|
||||
synchronized (this) {
|
||||
input = showInputDialog("Enter series name:", suggestion, files.get(0).getParentFile().getName(), window);
|
||||
input = showMultiValueInputDialog("Enter series name:", suggestion, files.get(0).getParentFile().getName(), parent);
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
if (input.size() > 0) {
|
||||
// only allow one fetch session at a time so later requests can make use of cached results
|
||||
synchronized (provider) {
|
||||
episodes = fetchEpisodeSet(singleton(input), locale, window);
|
||||
episodes = fetchEpisodeSet(input, locale, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,5 +268,4 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
|
||||
return matches;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import static net.sourceforge.filebot.MediaTypes.*;
|
|||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.awt.Window;
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -53,7 +53,7 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
|
||||
|
||||
@Override
|
||||
public List<Match<File, ?>> match(final List<File> files, Locale locale, boolean autodetect, Window window) throws Exception {
|
||||
public List<Match<File, ?>> match(final List<File> files, Locale locale, boolean autodetect, Component parent) throws Exception {
|
||||
// handle movie files
|
||||
File[] movieFiles = filter(files, VIDEO_FILES).toArray(new File[0]);
|
||||
|
||||
|
@ -70,7 +70,7 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
|
||||
// unknown hash, try via imdb id from nfo file
|
||||
if (movie == null || !autodetect) {
|
||||
movie = grabMovieName(movieFiles[i], locale, autodetect, window, movie);
|
||||
movie = grabMovieName(movieFiles[i], locale, autodetect, parent, movie);
|
||||
|
||||
if (movie != null) {
|
||||
Analytics.trackEvent(service.getName(), "SearchMovie", movie.toString(), 1);
|
||||
|
@ -161,7 +161,7 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
}
|
||||
|
||||
|
||||
protected Movie grabMovieName(File movieFile, Locale locale, boolean autodetect, Window window, Movie... suggestions) throws Exception {
|
||||
protected Movie grabMovieName(File movieFile, Locale locale, boolean autodetect, Component parent, Movie... suggestions) throws Exception {
|
||||
List<Movie> options = new ArrayList<Movie>();
|
||||
|
||||
// add default value if any
|
||||
|
@ -200,7 +200,7 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
|
||||
String input = null;
|
||||
synchronized (this) {
|
||||
input = showInputDialog("Enter movie name:", suggestion, options.get(0).getName(), window);
|
||||
input = showInputDialog("Enter movie name:", suggestion, options.get(0).getName(), parent);
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
|
@ -210,11 +210,11 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
return options.isEmpty() ? null : selectMovie(options, window);
|
||||
return options.isEmpty() ? null : selectMovie(options, parent);
|
||||
}
|
||||
|
||||
|
||||
protected Movie selectMovie(final List<Movie> options, final Window window) throws Exception {
|
||||
protected Movie selectMovie(final List<Movie> options, final Component parent) throws Exception {
|
||||
if (options.size() == 1) {
|
||||
return options.get(0);
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
@Override
|
||||
public Movie call() throws Exception {
|
||||
// multiple results have been found, user must select one
|
||||
SelectDialog<Movie> selectDialog = new SelectDialog<Movie>(window, options);
|
||||
SelectDialog<Movie> selectDialog = new SelectDialog<Movie>(parent, options);
|
||||
|
||||
selectDialog.getHeaderLabel().setText("Select Movie:");
|
||||
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore");
|
||||
|
|
|
@ -4,8 +4,11 @@ package net.sourceforge.filebot.ui.subtitle;
|
|||
|
||||
import static javax.swing.BorderFactory.*;
|
||||
import static javax.swing.JOptionPane.*;
|
||||
import static net.sourceforge.filebot.similarity.SeriesNameMatcher.*;
|
||||
import static net.sourceforge.filebot.subtitle.SubtitleUtilities.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
import static net.sourceforge.tuned.StringUtilities.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
|
@ -59,9 +62,7 @@ import net.sourceforge.filebot.ResourceManager;
|
|||
import net.sourceforge.filebot.similarity.EpisodeMetrics;
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.similarity.Matcher;
|
||||
import net.sourceforge.filebot.similarity.SeriesNameMatcher;
|
||||
import net.sourceforge.filebot.similarity.SimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.Language;
|
||||
import net.sourceforge.filebot.vfs.MemoryFile;
|
||||
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
||||
import net.sourceforge.filebot.web.SubtitleProvider;
|
||||
|
@ -73,7 +74,7 @@ import net.sourceforge.tuned.ui.LinkButton;
|
|||
import net.sourceforge.tuned.ui.RoundBorder;
|
||||
|
||||
|
||||
class VideoHashSubtitleDownloadDialog extends JDialog {
|
||||
class SubtitleAutoMatchDialog extends JDialog {
|
||||
|
||||
private final JPanel hashMatcherServicePanel = createServicePanel(0xFAFAD2); // LightGoldenRodYellow
|
||||
private final JPanel nameMatcherServicePanel = createServicePanel(0xFFEBCD); // BlanchedAlmond
|
||||
|
@ -85,8 +86,8 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
private ExecutorService downloadService;
|
||||
|
||||
|
||||
public VideoHashSubtitleDownloadDialog(Window owner) {
|
||||
super(owner, "Download Subtitles", ModalityType.MODELESS);
|
||||
public SubtitleAutoMatchDialog(Window owner) {
|
||||
super(owner, "Download Subtitles", ModalityType.DOCUMENT_MODAL);
|
||||
|
||||
JComponent content = (JComponent) getContentPane();
|
||||
content.setLayout(new MigLayout("fill, insets dialog, nogrid", "", "[fill][pref!]"));
|
||||
|
@ -189,8 +190,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
|
||||
public void startQuery(String languageName) {
|
||||
final SubtitleMappingTableModel mappingModel = (SubtitleMappingTableModel) subtitleMappingTable.getModel();
|
||||
|
||||
QueryTask queryTask = new QueryTask(services, mappingModel.getVideoFiles(), languageName) {
|
||||
QueryTask queryTask = new QueryTask(services, mappingModel.getVideoFiles(), languageName, SubtitleAutoMatchDialog.this) {
|
||||
|
||||
@Override
|
||||
protected void process(List<Map<File, List<SubtitleDescriptorBean>>> sequence) {
|
||||
|
@ -212,8 +212,8 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
}
|
||||
};
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1);
|
||||
executor.submit(queryTask);
|
||||
queryService = Executors.newFixedThreadPool(1);
|
||||
queryService.submit(queryTask);
|
||||
}
|
||||
|
||||
|
||||
|
@ -232,7 +232,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
JOptionPane optionPane = new JOptionPane(message, WARNING_MESSAGE, YES_NO_CANCEL_OPTION, null, options);
|
||||
|
||||
// display option dialog
|
||||
optionPane.createDialog(VideoHashSubtitleDownloadDialog.this, "Replace").setVisible(true);
|
||||
optionPane.createDialog(SubtitleAutoMatchDialog.this, "Replace").setVisible(true);
|
||||
|
||||
// replace all
|
||||
if (options[0] == optionPane.getValue())
|
||||
|
@ -277,7 +277,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
try {
|
||||
mapping.setSubtitleFile(get());
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(VideoHashSubtitleDownloadDialog.class.getName()).log(Level.WARNING, e.getMessage());
|
||||
Logger.getLogger(SubtitleAutoMatchDialog.class.getName()).log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -642,7 +642,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
|
||||
|
||||
public String getText() {
|
||||
return formatSubtitle(descriptor.getName(), getLanguage(), getType());
|
||||
return formatSubtitle(descriptor.getName(), getLanguageName(), getType());
|
||||
}
|
||||
|
||||
|
||||
|
@ -651,8 +651,8 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
}
|
||||
|
||||
|
||||
public String getLanguage() {
|
||||
return Language.getISO3LanguageCodeByName(descriptor.getLanguageName());
|
||||
public String getLanguageName() {
|
||||
return descriptor.getLanguageName();
|
||||
}
|
||||
|
||||
|
||||
|
@ -706,13 +706,15 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
|
||||
private static class QueryTask extends SwingWorker<Collection<File>, Map<File, List<SubtitleDescriptorBean>>> {
|
||||
|
||||
private final Component parent;
|
||||
private final Collection<SubtitleServiceBean> services;
|
||||
|
||||
private final Collection<File> remainingVideos;
|
||||
private final String languageName;
|
||||
|
||||
|
||||
public QueryTask(Collection<SubtitleServiceBean> services, Collection<File> videoFiles, String languageName) {
|
||||
public QueryTask(Collection<SubtitleServiceBean> services, Collection<File> videoFiles, String languageName, Component parent) {
|
||||
this.parent = parent;
|
||||
this.services = services;
|
||||
this.remainingVideos = new TreeSet<File>(videoFiles);
|
||||
this.languageName = languageName;
|
||||
|
@ -722,16 +724,17 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
@Override
|
||||
protected Collection<File> doInBackground() throws Exception {
|
||||
for (SubtitleServiceBean service : services) {
|
||||
try {
|
||||
if (isCancelled())
|
||||
if (isCancelled() || Thread.interrupted()) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
|
||||
if (remainingVideos.isEmpty())
|
||||
if (remainingVideos.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
Map<File, List<SubtitleDescriptorBean>> subtitleSet = new HashMap<File, List<SubtitleDescriptorBean>>();
|
||||
|
||||
for (final Entry<File, List<SubtitleDescriptor>> result : service.lookupSubtitles(remainingVideos, languageName).entrySet()) {
|
||||
for (final Entry<File, List<SubtitleDescriptor>> result : service.lookupSubtitles(remainingVideos, languageName, parent).entrySet()) {
|
||||
List<SubtitleDescriptorBean> subtitles = new ArrayList<SubtitleDescriptorBean>();
|
||||
|
||||
// associate subtitles with services
|
||||
|
@ -750,8 +753,15 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
}
|
||||
|
||||
publish(subtitleSet);
|
||||
} catch (CancellationException e) {
|
||||
// don't ignore cancellation
|
||||
throw e;
|
||||
} catch (InterruptedException e) {
|
||||
// don't ignore cancellation
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(VideoHashSubtitleDownloadDialog.class.getName()).log(Level.WARNING, e.getMessage());
|
||||
// log and ignore
|
||||
Logger.getLogger(SubtitleAutoMatchDialog.class.getName()).log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -781,9 +791,10 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
if (descriptor.getType() == null && subtitle == null)
|
||||
return null;
|
||||
|
||||
// prefer type from descriptor because we need to know before we download the actual subtitle file
|
||||
String base = FileUtilities.getName(video);
|
||||
String ext = descriptor.getType() != null ? descriptor.getType() : getExtension(subtitle.getName());
|
||||
return new File(video.getParentFile(), formatSubtitle(base, descriptor.getLanguage(), ext));
|
||||
String ext = (descriptor.getType() != null) ? descriptor.getType() : getExtension(subtitle.getName());
|
||||
return new File(video.getParentFile(), formatSubtitle(base, descriptor.getLanguageName(), ext));
|
||||
}
|
||||
|
||||
|
||||
|
@ -802,7 +813,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
|
||||
return destination;
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(VideoHashSubtitleDownloadDialog.class.getName()).log(Level.WARNING, e.getMessage());
|
||||
Logger.getLogger(SubtitleAutoMatchDialog.class.getName()).log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -842,14 +853,14 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
}
|
||||
|
||||
|
||||
protected abstract Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName) throws Exception;
|
||||
protected abstract Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception;
|
||||
|
||||
|
||||
public final Map<File, List<SubtitleDescriptor>> lookupSubtitles(Collection<File> files, String languageName) throws Exception {
|
||||
public final Map<File, List<SubtitleDescriptor>> lookupSubtitles(Collection<File> files, String languageName, Component parent) throws Exception {
|
||||
setState(StateValue.STARTED);
|
||||
|
||||
try {
|
||||
return getSubtitleList(files, languageName);
|
||||
return getSubtitleList(files, languageName, parent);
|
||||
} catch (Exception e) {
|
||||
// remember error
|
||||
error = e;
|
||||
|
@ -891,7 +902,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
|
||||
|
||||
@Override
|
||||
protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName) throws Exception {
|
||||
protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception {
|
||||
return service.getSubtitleList(files.toArray(new File[0]), languageName);
|
||||
}
|
||||
}
|
||||
|
@ -909,17 +920,29 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
|||
|
||||
|
||||
@Override
|
||||
protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName) throws Exception {
|
||||
protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception {
|
||||
Map<File, List<SubtitleDescriptor>> subtitlesByFile = new HashMap<File, List<SubtitleDescriptor>>();
|
||||
for (File file : files) {
|
||||
subtitlesByFile.put(file, new ArrayList<SubtitleDescriptor>());
|
||||
}
|
||||
|
||||
// auto-detect query and search for subtitles
|
||||
Collection<String> querySet = new SeriesNameMatcher().matchAll(files.toArray(new File[0]));
|
||||
Collection<String> querySet = detectSeriesName(files);
|
||||
List<SubtitleDescriptor> subtitles = findSubtitles(service, querySet, languageName);
|
||||
|
||||
// if auto-detection fails, ask user for input
|
||||
if (subtitles.isEmpty()) {
|
||||
throw new IllegalArgumentException("Unable to lookup subtitles:" + querySet);
|
||||
// dialog may have been cancelled by now
|
||||
if (Thread.interrupted())
|
||||
throw new CancellationException();
|
||||
|
||||
querySet = showMultiValueInputDialog("Enter series / movie names:", join(querySet, ","), service.getName(), parent);
|
||||
subtitles = findSubtitles(service, querySet, languageName);
|
||||
|
||||
// still no luck... na women ye mei banfa
|
||||
if (subtitles.isEmpty()) {
|
||||
throw new Exception("Unable to lookup subtitles:" + querySet);
|
||||
}
|
||||
}
|
||||
|
||||
// first match everything as best as possible, then filter possibly bad matches
|
|
@ -96,7 +96,7 @@ abstract class SubtitleDropTarget extends JButton {
|
|||
|
||||
|
||||
private boolean handleDownload(List<File> videoFiles) {
|
||||
VideoHashSubtitleDownloadDialog dialog = new VideoHashSubtitleDownloadDialog(getWindow(this));
|
||||
SubtitleAutoMatchDialog dialog = new SubtitleAutoMatchDialog(getWindow(this));
|
||||
|
||||
// initialize download parameters
|
||||
dialog.setVideoFiles(videoFiles.toArray(new File[0]));
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.util.logging.Logger;
|
|||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import redstone.xmlrpc.XmlRpcException;
|
||||
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.web.OpenSubtitlesXmlRpc.Query;
|
||||
import net.sourceforge.tuned.Timer;
|
||||
|
@ -65,10 +67,14 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
|||
// require login
|
||||
login();
|
||||
|
||||
try {
|
||||
// search for movies / series
|
||||
SearchResult[] result = xmlrpc.searchMoviesOnIMDB(query).toArray(new SearchResult[0]);
|
||||
|
||||
return Arrays.asList(result);
|
||||
List<Movie> resultSet = xmlrpc.searchMoviesOnIMDB(query);
|
||||
return Arrays.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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,23 +2,20 @@
|
|||
package net.sourceforge.tuned;
|
||||
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
public final class StringUtilities {
|
||||
|
||||
public static boolean isEmptyValue(Object object) {
|
||||
return object != null && object.toString().length() > 0;
|
||||
}
|
||||
|
||||
|
||||
public static String join(Object[] values, CharSequence delimiter) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
sb.append(values[i]);
|
||||
|
||||
if (i < values.length - 1) {
|
||||
sb.append(delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
return join(asList(values), delimiter);
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,11 +23,14 @@ public final class StringUtilities {
|
|||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Iterator<?> iterator = values.iterator(); iterator.hasNext();) {
|
||||
sb.append(iterator.next());
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
Object value = iterator.next();
|
||||
if (!isEmptyValue(value)) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(delimiter);
|
||||
}
|
||||
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
package net.sourceforge.tuned.ui;
|
||||
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static javax.swing.JOptionPane.*;
|
||||
|
||||
import java.awt.Color;
|
||||
|
@ -18,6 +19,8 @@ import java.awt.event.ActionListener;
|
|||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
|
@ -118,7 +121,32 @@ public final class TunedUtilities {
|
|||
}
|
||||
|
||||
|
||||
public static String showInputDialog(final String text, final String initialValue, final String title, final Window parent) throws InvocationTargetException, InterruptedException {
|
||||
public static List<String> showMultiValueInputDialog(final String text, final String initialValue, final String title, final Component parent) throws InvocationTargetException, InterruptedException {
|
||||
String input = showInputDialog(text, initialValue, title, parent);
|
||||
if (input == null || input.isEmpty()) {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
for (char separator : new char[] { '|', ';', ',' }) {
|
||||
if (input.indexOf(separator) >= 0) {
|
||||
List<String> values = new ArrayList<String>();
|
||||
for (String field : input.split(Character.toString(separator))) {
|
||||
if (field.length() > 0) {
|
||||
values.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (values.size() > 0) {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return singletonList(input);
|
||||
}
|
||||
|
||||
|
||||
public static String showInputDialog(final String text, final String initialValue, final String title, final Component parent) throws InvocationTargetException, InterruptedException {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
|
||||
|
|
Loading…
Reference in New Issue