* play with new convenience methods for Swing using lambdas

This commit is contained in:
Reinhard Pointner 2016-02-06 14:03:56 +00:00
parent fde21946dc
commit 15dc273d7f
2 changed files with 183 additions and 133 deletions

View File

@ -1,6 +1,7 @@
package net.filebot.ui.subtitle.upload;
import static net.filebot.media.MediaDetection.*;
import static net.filebot.util.ui.SwingUI.*;
import java.awt.Color;
import java.awt.Dimension;
@ -21,14 +22,10 @@ import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import net.filebot.Language;
import net.filebot.ResourceManager;
@ -64,8 +61,8 @@ public class SubtitleUploadDialog extends JDialog {
content.add(new JScrollPane(subtitleMappingTable), "grow, wrap");
content.add(new JButton(uploadAction), "tag ok");
content.add(new JButton(finishAction), "tag cancel");
content.add(newButton("Upload", ResourceManager.getIcon("dialog.continue"), this::doUpload), "tag ok");
content.add(newButton("Close", ResourceManager.getIcon("dialog.cancel"), this::doClose), "tag cancel");
}
protected JTable createTable() {
@ -108,10 +105,9 @@ public class SubtitleUploadDialog extends JDialog {
}
public void startChecking() {
SubtitleMapping[] data = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
for (SubtitleMapping it : data) {
if (it.isCheckReady()) {
checkExecutorService.submit(new CheckTask(it));
for (SubtitleMapping mapping : ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData()) {
if (mapping.isCheckReady()) {
checkExecutorService.submit(() -> runCheck(mapping));
}
}
}
@ -135,153 +131,121 @@ public class SubtitleUploadDialog extends JDialog {
}).toList();
}
private final Action uploadAction = new AbstractAction("Upload", ResourceManager.getIcon("dialog.continue")) {
private void runCheck(SubtitleMapping mapping) {
try {
@Override
public void actionPerformed(ActionEvent evt) {
// disable any active cell editor
if (subtitleMappingTable.getCellEditor() != null) {
subtitleMappingTable.getCellEditor().stopCellEditing();
}
if (mapping.getIdentity() == null && mapping.getVideo() != null) {
mapping.setState(Status.Checking);
// don't allow restart of upload as long as there are still unfinished download tasks
if (uploadExecutorService != null && !uploadExecutorService.isTerminated()) {
return;
}
CheckResult checkResult = database.checkSubtitle(mapping.getVideo(), mapping.getSubtitle());
uploadExecutorService = Executors.newSingleThreadExecutor();
SubtitleMapping[] table = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
for (SubtitleGroup it : groupRunsByCD(table)) {
if (it.isUploadReady()) {
uploadExecutorService.submit(new UploadTask(it));
// force upload all subtitles regardless of what TryUploadSubtitles returns (because other programs often submit crap)
if (checkResult.exists) {
mapping.setLanguage(Language.getLanguage(checkResult.language)); // trust language hint only if upload not required
}
}
// terminate after all uploads have been completed
uploadExecutorService.shutdown();
}
};
private final Action finishAction = new AbstractAction("Close", ResourceManager.getIcon("dialog.cancel")) {
@Override
public void actionPerformed(ActionEvent evt) {
if (checkExecutorService != null) {
checkExecutorService.shutdownNow();
}
if (uploadExecutorService != null) {
uploadExecutorService.shutdownNow();
if (mapping.getLanguage() == null) {
mapping.setState(Status.Identifying);
try {
Locale locale = database.detectLanguage(FileUtilities.readFile(mapping.getSubtitle()));
mapping.setLanguage(Language.getLanguage(locale));
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to auto-detect language: " + e.getMessage());
}
}
setVisible(false);
dispose();
}
};
private class CheckTask extends SwingWorker<Object, Void> {
private final SubtitleMapping mapping;
public CheckTask(SubtitleMapping mapping) {
this.mapping = mapping;
}
@Override
protected Object doInBackground() throws Exception {
try {
if (mapping.getIdentity() == null && mapping.getVideo() != null) {
mapping.setState(Status.Checking);
CheckResult checkResult = database.checkSubtitle(mapping.getVideo(), mapping.getSubtitle());
// force upload all subtitles regardless of what TryUploadSubtitles returns (because other programs often submit crap)
if (checkResult.exists) {
mapping.setLanguage(Language.getLanguage(checkResult.language)); // trust language hint only if upload not required
}
}
if (mapping.getLanguage() == null) {
mapping.setState(Status.Identifying);
try {
Locale locale = database.detectLanguage(FileUtilities.readFile(mapping.getSubtitle()));
mapping.setLanguage(Language.getLanguage(locale));
} catch (Exception e) {
Logger.getLogger(CheckTask.class.getClass().getName()).log(Level.WARNING, "Failed to auto-detect language: " + e.getMessage());
}
}
if (mapping.getIdentity() == null && mapping.getVideo() != null) {
mapping.setState(Status.Identifying);
try {
if (MediaDetection.isEpisode(mapping.getVideo().getPath(), true)) {
List<String> seriesNames = MediaDetection.detectSeriesNames(Collections.singleton(mapping.getVideo()), true, false, Locale.ENGLISH);
NAMES: for (String name : seriesNames) {
List<SearchResult> options = WebServices.TheTVDB.search(name, Locale.ENGLISH);
for (SearchResult entry : options) {
TheTVDBSeriesInfo seriesInfo = (TheTVDBSeriesInfo) WebServices.TheTVDB.getSeriesInfo(entry, Locale.ENGLISH);
if (seriesInfo.getImdbId() != null) {
int imdbId = grepImdbId(seriesInfo.getImdbId()).iterator().next();
mapping.setIdentity(WebServices.OpenSubtitles.getMovieDescriptor(new Movie(null, 0, imdbId, -1), Locale.ENGLISH));
break NAMES;
}
}
}
} else {
Collection<Movie> identity = MediaDetection.detectMovie(mapping.getVideo(), database, Locale.ENGLISH, true);
for (Movie it : identity) {
if (it.getImdbId() <= 0 && it.getTmdbId() > 0) {
it = WebServices.TheMovieDB.getMovieDescriptor(it, Locale.ENGLISH);
}
if (it != null && it.getImdbId() > 0) {
mapping.setIdentity(it);
break;
if (mapping.getIdentity() == null && mapping.getVideo() != null) {
mapping.setState(Status.Identifying);
try {
if (MediaDetection.isEpisode(mapping.getVideo().getPath(), true)) {
List<String> seriesNames = MediaDetection.detectSeriesNames(Collections.singleton(mapping.getVideo()), true, false, Locale.ENGLISH);
NAMES: for (String name : seriesNames) {
List<SearchResult> options = WebServices.TheTVDB.search(name, Locale.ENGLISH);
for (SearchResult entry : options) {
TheTVDBSeriesInfo seriesInfo = (TheTVDBSeriesInfo) WebServices.TheTVDB.getSeriesInfo(entry, Locale.ENGLISH);
if (seriesInfo.getImdbId() != null) {
int imdbId = grepImdbId(seriesInfo.getImdbId()).iterator().next();
mapping.setIdentity(WebServices.OpenSubtitles.getMovieDescriptor(new Movie(null, 0, imdbId, -1), Locale.ENGLISH));
break NAMES;
}
}
}
} catch (Exception e) {
Logger.getLogger(CheckTask.class.getClass().getName()).log(Level.WARNING, "Failed to auto-detect movie: " + e.getMessage());
} else {
Collection<Movie> identity = MediaDetection.detectMovie(mapping.getVideo(), database, Locale.ENGLISH, true);
for (Movie it : identity) {
if (it.getImdbId() <= 0 && it.getTmdbId() > 0) {
it = WebServices.TheMovieDB.getMovieDescriptor(it, Locale.ENGLISH);
}
if (it != null && it.getImdbId() > 0) {
mapping.setIdentity(it);
break;
}
}
}
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to auto-detect movie: " + e.getMessage());
}
if (mapping.getVideo() == null) {
mapping.setState(Status.IllegalInput);
} else if (mapping.getIdentity() == null || mapping.getLanguage() == null) {
mapping.setState(Status.IdentificationRequired);
} else {
mapping.setState(Status.UploadReady);
}
} catch (Exception e) {
Logger.getLogger(CheckTask.class.getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
mapping.setState(Status.CheckFailed);
}
return null;
if (mapping.getVideo() == null) {
mapping.setState(Status.IllegalInput);
} else if (mapping.getIdentity() == null || mapping.getLanguage() == null) {
mapping.setState(Status.IdentificationRequired);
} else {
mapping.setState(Status.UploadReady);
}
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
mapping.setState(Status.CheckFailed);
}
}
private class UploadTask extends SwingWorker<Object, Void> {
private void runUpload(SubtitleGroup group) {
try {
group.setState(Status.Uploading);
database.uploadSubtitle(group.getIdentity(), group.getLanguage().getLocale(), group.getVideoFiles(), group.getSubtitleFiles());
group.setState(Status.UploadComplete);
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
group.setState(Status.UploadFailed);
}
}
private final SubtitleGroup uploadGroup;
public UploadTask(SubtitleGroup uploadGroup) {
this.uploadGroup = uploadGroup;
public void doUpload(ActionEvent evt) {
// disable any active cell editor
if (subtitleMappingTable.getCellEditor() != null) {
subtitleMappingTable.getCellEditor().stopCellEditing();
}
@Override
protected Object doInBackground() {
try {
uploadGroup.setState(Status.Uploading);
// don't allow restart of upload as long as there are still unfinished download tasks
if (uploadExecutorService != null && !uploadExecutorService.isTerminated()) {
return;
}
database.uploadSubtitle(uploadGroup.getIdentity(), uploadGroup.getLanguage().getLocale(), uploadGroup.getVideoFiles(), uploadGroup.getSubtitleFiles());
uploadExecutorService = Executors.newSingleThreadExecutor();
uploadGroup.setState(Status.UploadComplete);
} catch (Exception e) {
Logger.getLogger(UploadTask.class.getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
uploadGroup.setState(Status.UploadFailed);
SubtitleMapping[] table = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
for (SubtitleGroup group : groupRunsByCD(table)) {
if (group.isUploadReady()) {
uploadExecutorService.submit(() -> runUpload(group));
}
return null;
}
// terminate after all uploads have been completed
uploadExecutorService.shutdown();
}
public void doClose(ActionEvent evt) {
if (checkExecutorService != null) {
checkExecutorService.shutdownNow();
}
if (uploadExecutorService != null) {
uploadExecutorService.shutdownNow();
}
setVisible(false);
dispose();
}
}

View File

@ -20,6 +20,8 @@ import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
@ -33,6 +35,7 @@ import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.basic.BasicTableUI;
@ -266,6 +269,89 @@ public final class SwingUI {
return timer;
}
public static JButton newButton(String name, Icon icon, Consumer<ActionEvent> action) {
return new JButton(new LambdaAction(name, icon, action));
}
public static Action newAction(String name, Icon icon, Consumer<ActionEvent> action) {
return new LambdaAction(name, icon, action);
}
private static class LambdaAction extends AbstractAction {
private Consumer<ActionEvent> action;
public LambdaAction(String name, Icon icon, Consumer<ActionEvent> action) {
super(name, icon);
this.action = action;
}
@Override
public void actionPerformed(ActionEvent e) {
action.accept(e);
}
}
public static SwingWorker<Void, Void> newSwingWorker(BackgroundRunnable doInBackground) {
return new SwingRunnable(doInBackground);
}
public static <T> SwingWorker<T, Void> newSwingWorker(BackgroundSupplier<T> doInBackground, Consumer<T> done, Consumer<Exception> error) {
return new SwingLambda<T, Void>(doInBackground, done, error);
}
private static class SwingRunnable extends SwingWorker<Void, Void> {
private BackgroundRunnable doInBackground;
public SwingRunnable(BackgroundRunnable doInBackground) {
this.doInBackground = doInBackground;
}
@Override
protected Void doInBackground() throws Exception {
doInBackground.run();
return null;
}
}
@FunctionalInterface
public static interface BackgroundRunnable {
void run() throws Exception;
}
@FunctionalInterface
public static interface BackgroundSupplier<T> {
T get() throws Exception;
}
private static class SwingLambda<T, V> extends SwingWorker<T, V> {
private BackgroundSupplier<T> doInBackground;
private Consumer<T> done;
private Consumer<Exception> error;
public SwingLambda(BackgroundSupplier<T> doInBackground, Consumer<T> done, Consumer<Exception> error) {
this.doInBackground = doInBackground;
this.done = done;
this.error = error;
}
@Override
protected T doInBackground() throws Exception {
return doInBackground.get();
}
@Override
protected void done() {
try {
done.accept(get());
} catch (InterruptedException | ExecutionException e) {
error.accept(e);
}
}
}
/**
* When trying to drag a row of a multi-select JTable, it will start selecting rows instead of initiating a drag. This TableUI will give the JTable proper dnd behaviour.
*/