diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java b/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java index 709ddc8f..efe0000c 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java @@ -4,7 +4,6 @@ package net.sourceforge.filebot.ui.panel.subtitle; import static javax.swing.BorderFactory.*; import static javax.swing.JOptionPane.*; -import static net.sourceforge.filebot.ui.NotificationLogging.*; import static net.sourceforge.filebot.ui.panel.subtitle.SubtitleUtilities.*; import java.awt.Color; @@ -70,6 +69,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { private final JTable subtitleMappingTable = createTable(); + private ExecutorService queryService; private ExecutorService downloadService; @@ -158,7 +158,9 @@ class VideoHashSubtitleDownloadDialog extends JDialog { public void startQuery(String languageName) { final SubtitleMappingTableModel mappingModel = (SubtitleMappingTableModel) subtitleMappingTable.getModel(); - // query services concurrently + // query services sequentially + queryService = Executors.newFixedThreadPool(1); + for (VideoHashSubtitleServiceBean service : services) { QueryTask task = new QueryTask(service, mappingModel.getVideoFiles(), languageName) { @@ -179,13 +181,13 @@ class VideoHashSubtitleDownloadDialog extends JDialog { // make subtitle column visible mappingModel.setOptionColumnVisible(true); } catch (Exception e) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage(), e); + Logger.getLogger(VideoHashSubtitleDownloadDialog.class.getName()).log(Level.WARNING, e.getMessage()); } } }; // start background worker - task.execute(); + queryService.submit(task); } } @@ -292,6 +294,10 @@ class VideoHashSubtitleDownloadDialog extends JDialog { @Override public void actionPerformed(ActionEvent evt) { + if (queryService != null) { + queryService.shutdownNow(); + } + if (downloadService != null) { downloadService.shutdownNow(); } @@ -716,7 +722,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { return destination; } catch (Exception e) { - UILogger.log(Level.WARNING, e.getMessage(), e); + Logger.getLogger(VideoHashSubtitleDownloadDialog.class.getName()).log(Level.WARNING, e.getMessage()); } return null; diff --git a/source/net/sourceforge/filebot/web/SublightSubtitleClient.java b/source/net/sourceforge/filebot/web/SublightSubtitleClient.java index 36d74622..9bbced2d 100644 --- a/source/net/sourceforge/filebot/web/SublightSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SublightSubtitleClient.java @@ -2,6 +2,9 @@ package net.sourceforge.filebot.web; +import static java.lang.Math.*; +import static java.util.Collections.*; + import java.io.File; import java.io.IOException; import java.net.URI; @@ -11,7 +14,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.Icon; import javax.xml.ws.Holder; @@ -116,35 +125,74 @@ public class SublightSubtitleClient implements SubtitleProvider, VideoHashSubtit public Map> getSubtitleList(File[] files, final String languageName) throws Exception { Map> subtitles = new HashMap>(files.length); - for (final File file : files) { - subtitles.put(file, getSubtitleList(file, languageName)); - } - - return subtitles; - } - - - public List getSubtitleList(File videoFile, String languageName) throws WebServiceException, IOException { - List subtitles = new ArrayList(); + ExecutorService executor = Executors.newFixedThreadPool(min(files.length, 10)); + List>> requests = new ArrayList>>(); try { - // retrieve subtitles by video hash - for (Subtitle subtitle : getSubtitleList(SublightVideoHasher.computeHash(videoFile), null, null, languageName)) { - // only keep linked subtitles - if (subtitle.isIsLinked()) { - subtitles.add(new SublightSubtitleDescriptor(subtitle, this)); + // queue and execute requests + for (int i = 0; i < files.length; i++) { + Future> request = null; + + try { + // make call interruptible + if (Thread.interrupted()) + throw new InterruptedException(); + + // compute video hash and execute query in parallel + final String videoHash = SublightVideoHasher.computeHash(files[i]); + + request = executor.submit(new Callable>() { + + @Override + public List call() throws Exception { + return getSubtitleList(videoHash, languageName); + } + }); + } catch (IOException e) { + Logger.getLogger(SublightSubtitleClient.class.getName()).log(Level.WARNING, "Error computing video hash: " + e.getMessage()); + } catch (LinkageError e) { + // MediaInfo native lib not available + throw new UnsupportedOperationException(e.getMessage(), e); } + + requests.add(i, request); + } + + // collect results + for (int i = 0; i < files.length; i++) { + List response = emptyList(); + + if (requests.get(i) != null) { + response = requests.get(i).get(); + } + + subtitles.put(files[i], response); + } + + return subtitles; + } finally { + // shutdown after all tasks are done or an exception was thrown + executor.shutdownNow(); + } + } + + + public List getSubtitleList(String videoHash, String languageName) throws WebServiceException { + List subtitles = new ArrayList(); + + // retrieve subtitles by video hash + for (Subtitle subtitle : getSubtitleList(videoHash, null, null, languageName)) { + // only keep linked subtitles + if (subtitle.isIsLinked()) { + subtitles.add(new SublightSubtitleDescriptor(subtitle, this)); } - } catch (LinkageError e) { - // MediaInfo native lib not available - throw new UnsupportedOperationException(e.getMessage(), e); } return subtitles; } - protected List getSubtitleList(String videoHash, String name, Integer year, String languageName) throws WebServiceException { + public List getSubtitleList(String videoHash, String name, Integer year, String languageName) throws WebServiceException { // require login login(); @@ -277,7 +325,7 @@ public class SublightSubtitleClient implements SubtitleProvider, VideoHashSubtit } - protected byte[] getZipArchive(Subtitle subtitle) throws WebServiceException, InterruptedException { + protected synchronized byte[] getZipArchive(Subtitle subtitle) throws WebServiceException, InterruptedException { // require login login(); diff --git a/source/net/sourceforge/filebot/web/SublightVideoHasher.java b/source/net/sourceforge/filebot/web/SublightVideoHasher.java index c68fd136..0a2919f9 100644 --- a/source/net/sourceforge/filebot/web/SublightVideoHasher.java +++ b/source/net/sourceforge/filebot/web/SublightVideoHasher.java @@ -32,7 +32,6 @@ import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind; */ public final class SublightVideoHasher { - public static String computeHash(File file) throws IOException, LinkageError { byte[][] hash = new byte[4][]; @@ -92,6 +91,10 @@ public final class SublightVideoHasher { // close handle mediaInfo.close(); + // sanity check + if (duration.isEmpty()) + throw new IOException("Failed to read video duration"); + // convert from milliseconds to given unit return unit.convert(Long.parseLong(duration), TimeUnit.MILLISECONDS); }