mirror of
https://github.com/mitb-archive/filebot
synced 2024-11-18 07:15:07 -05:00
* play with new convenience methods for Swing using lambdas
This commit is contained in:
parent
fde21946dc
commit
15dc273d7f
@ -1,6 +1,7 @@
|
|||||||
package net.filebot.ui.subtitle.upload;
|
package net.filebot.ui.subtitle.upload;
|
||||||
|
|
||||||
import static net.filebot.media.MediaDetection.*;
|
import static net.filebot.media.MediaDetection.*;
|
||||||
|
import static net.filebot.util.ui.SwingUI.*;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
@ -21,14 +22,10 @@ import java.util.logging.Logger;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.Action;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.SwingWorker;
|
|
||||||
|
|
||||||
import net.filebot.Language;
|
import net.filebot.Language;
|
||||||
import net.filebot.ResourceManager;
|
import net.filebot.ResourceManager;
|
||||||
@ -64,8 +61,8 @@ public class SubtitleUploadDialog extends JDialog {
|
|||||||
|
|
||||||
content.add(new JScrollPane(subtitleMappingTable), "grow, wrap");
|
content.add(new JScrollPane(subtitleMappingTable), "grow, wrap");
|
||||||
|
|
||||||
content.add(new JButton(uploadAction), "tag ok");
|
content.add(newButton("Upload", ResourceManager.getIcon("dialog.continue"), this::doUpload), "tag ok");
|
||||||
content.add(new JButton(finishAction), "tag cancel");
|
content.add(newButton("Close", ResourceManager.getIcon("dialog.cancel"), this::doClose), "tag cancel");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected JTable createTable() {
|
protected JTable createTable() {
|
||||||
@ -108,10 +105,9 @@ public class SubtitleUploadDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startChecking() {
|
public void startChecking() {
|
||||||
SubtitleMapping[] data = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
|
for (SubtitleMapping mapping : ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData()) {
|
||||||
for (SubtitleMapping it : data) {
|
if (mapping.isCheckReady()) {
|
||||||
if (it.isCheckReady()) {
|
checkExecutorService.submit(() -> runCheck(mapping));
|
||||||
checkExecutorService.submit(new CheckTask(it));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,153 +131,121 @@ public class SubtitleUploadDialog extends JDialog {
|
|||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Action uploadAction = new AbstractAction("Upload", ResourceManager.getIcon("dialog.continue")) {
|
private void runCheck(SubtitleMapping mapping) {
|
||||||
|
try {
|
||||||
|
|
||||||
@Override
|
if (mapping.getIdentity() == null && mapping.getVideo() != null) {
|
||||||
public void actionPerformed(ActionEvent evt) {
|
mapping.setState(Status.Checking);
|
||||||
// disable any active cell editor
|
|
||||||
if (subtitleMappingTable.getCellEditor() != null) {
|
|
||||||
subtitleMappingTable.getCellEditor().stopCellEditing();
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't allow restart of upload as long as there are still unfinished download tasks
|
CheckResult checkResult = database.checkSubtitle(mapping.getVideo(), mapping.getSubtitle());
|
||||||
if (uploadExecutorService != null && !uploadExecutorService.isTerminated()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadExecutorService = Executors.newSingleThreadExecutor();
|
// force upload all subtitles regardless of what TryUploadSubtitles returns (because other programs often submit crap)
|
||||||
|
if (checkResult.exists) {
|
||||||
SubtitleMapping[] table = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
|
mapping.setLanguage(Language.getLanguage(checkResult.language)); // trust language hint only if upload not required
|
||||||
for (SubtitleGroup it : groupRunsByCD(table)) {
|
|
||||||
if (it.isUploadReady()) {
|
|
||||||
uploadExecutorService.submit(new UploadTask(it));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// terminate after all uploads have been completed
|
if (mapping.getLanguage() == null) {
|
||||||
uploadExecutorService.shutdown();
|
mapping.setState(Status.Identifying);
|
||||||
}
|
try {
|
||||||
};
|
Locale locale = database.detectLanguage(FileUtilities.readFile(mapping.getSubtitle()));
|
||||||
|
mapping.setLanguage(Language.getLanguage(locale));
|
||||||
private final Action finishAction = new AbstractAction("Close", ResourceManager.getIcon("dialog.cancel")) {
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to auto-detect language: " + e.getMessage());
|
||||||
@Override
|
}
|
||||||
public void actionPerformed(ActionEvent evt) {
|
|
||||||
if (checkExecutorService != null) {
|
|
||||||
checkExecutorService.shutdownNow();
|
|
||||||
}
|
|
||||||
if (uploadExecutorService != null) {
|
|
||||||
uploadExecutorService.shutdownNow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisible(false);
|
if (mapping.getIdentity() == null && mapping.getVideo() != null) {
|
||||||
dispose();
|
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);
|
||||||
private class CheckTask extends SwingWorker<Object, Void> {
|
NAMES: for (String name : seriesNames) {
|
||||||
|
List<SearchResult> options = WebServices.TheTVDB.search(name, Locale.ENGLISH);
|
||||||
private final SubtitleMapping mapping;
|
for (SearchResult entry : options) {
|
||||||
|
TheTVDBSeriesInfo seriesInfo = (TheTVDBSeriesInfo) WebServices.TheTVDB.getSeriesInfo(entry, Locale.ENGLISH);
|
||||||
public CheckTask(SubtitleMapping mapping) {
|
if (seriesInfo.getImdbId() != null) {
|
||||||
this.mapping = mapping;
|
int imdbId = grepImdbId(seriesInfo.getImdbId()).iterator().next();
|
||||||
}
|
mapping.setIdentity(WebServices.OpenSubtitles.getMovieDescriptor(new Movie(null, 0, imdbId, -1), Locale.ENGLISH));
|
||||||
|
break NAMES;
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} else {
|
||||||
Logger.getLogger(CheckTask.class.getClass().getName()).log(Level.WARNING, "Failed to auto-detect movie: " + e.getMessage());
|
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 void doUpload(ActionEvent evt) {
|
||||||
|
// disable any active cell editor
|
||||||
public UploadTask(SubtitleGroup uploadGroup) {
|
if (subtitleMappingTable.getCellEditor() != null) {
|
||||||
this.uploadGroup = uploadGroup;
|
subtitleMappingTable.getCellEditor().stopCellEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// don't allow restart of upload as long as there are still unfinished download tasks
|
||||||
protected Object doInBackground() {
|
if (uploadExecutorService != null && !uploadExecutorService.isTerminated()) {
|
||||||
try {
|
return;
|
||||||
uploadGroup.setState(Status.Uploading);
|
}
|
||||||
|
|
||||||
database.uploadSubtitle(uploadGroup.getIdentity(), uploadGroup.getLanguage().getLocale(), uploadGroup.getVideoFiles(), uploadGroup.getSubtitleFiles());
|
uploadExecutorService = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
uploadGroup.setState(Status.UploadComplete);
|
SubtitleMapping[] table = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
|
||||||
} catch (Exception e) {
|
for (SubtitleGroup group : groupRunsByCD(table)) {
|
||||||
Logger.getLogger(UploadTask.class.getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
|
if (group.isUploadReady()) {
|
||||||
uploadGroup.setState(Status.UploadFailed);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
@ -33,6 +35,7 @@ import javax.swing.KeyStroke;
|
|||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.basic.BasicTableUI;
|
import javax.swing.plaf.basic.BasicTableUI;
|
||||||
@ -266,6 +269,89 @@ public final class SwingUI {
|
|||||||
return timer;
|
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.
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user