mirror of
https://github.com/mitb-archive/filebot
synced 2024-12-23 00:08:51 -05:00
+ subtitle list displays found subtitles and download phase
+ download and extract selected subtitle packages (not displayed yet though) * SubtitleDescriptor provides download function (Callable) * updated sublight webservice * lazy-initialize SubtitlesAPI2Soap (Sublight) because loading all the jax-ws classes will can take more than 1s (while blocking EDT) * better sublight subtitle display names * added archive files to media.types * added icons for subtitle list * refactoring
This commit is contained in:
parent
b087fbc490
commit
18456f6864
BIN
fw/package.fetch.png
Normal file
BIN
fw/package.fetch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
Binary file not shown.
@ -2,10 +2,11 @@
|
||||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import java.io.FileFilter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.tuned.FileUtilities.ExtensionFileFilter;
|
||||
|
||||
|
||||
public final class FileBotUtilities {
|
||||
|
||||
@ -15,7 +16,7 @@ public final class FileBotUtilities {
|
||||
public static final String INVALID_CHARACTERS = "\\/:*?\"<>|\r\n";
|
||||
public static final Pattern INVALID_CHARACTERS_PATTERN = Pattern.compile(String.format("[%s]+", Pattern.quote(INVALID_CHARACTERS)));
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Strip filename of invalid characters
|
||||
*
|
||||
@ -32,6 +33,7 @@ public final class FileBotUtilities {
|
||||
return INVALID_CHARACTERS_PATTERN.matcher(filename).find();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link Pattern} that will match checksums enclosed in brackets ("[]" or "()"). A
|
||||
* checksum string is a hex number with at least 8 digits. Capturing group 0 will contain
|
||||
@ -39,7 +41,7 @@ public final class FileBotUtilities {
|
||||
*/
|
||||
public static final Pattern EMBEDDED_CHECKSUM_PATTERN = Pattern.compile("(?<=\\[|\\()(\\p{XDigit}{8,})(?=\\]|\\))");
|
||||
|
||||
|
||||
|
||||
public static String getEmbeddedChecksum(CharSequence string) {
|
||||
Matcher matcher = EMBEDDED_CHECKSUM_PATTERN.matcher(string);
|
||||
String embeddedChecksum = null;
|
||||
@ -57,13 +59,15 @@ public final class FileBotUtilities {
|
||||
return string.replaceAll("[\\(\\[]\\p{XDigit}{8}[\\]\\)]", "");
|
||||
}
|
||||
|
||||
public static final FileFilter TORRENT_FILES = MediaTypes.getDefault().filter("application/torrent");
|
||||
public static final FileFilter LIST_FILES = MediaTypes.getDefault().filter("application/list");
|
||||
public static final FileFilter VIDEO_FILES = MediaTypes.getDefault().filter("video");
|
||||
public static final FileFilter SUBTITLE_FILES = MediaTypes.getDefault().filter("subtitle");
|
||||
public static final FileFilter SFV_FILES = MediaTypes.getDefault().filter("verification/sfv");
|
||||
|
||||
|
||||
public static final ExtensionFileFilter TORRENT_FILES = MediaTypes.getDefault().filter("application/torrent");
|
||||
public static final ExtensionFileFilter LIST_FILES = MediaTypes.getDefault().filter("application/list");
|
||||
public static final ExtensionFileFilter VIDEO_FILES = MediaTypes.getDefault().filter("video");
|
||||
public static final ExtensionFileFilter SUBTITLE_FILES = MediaTypes.getDefault().filter("subtitle");
|
||||
public static final ExtensionFileFilter ARCHIVE_FILES = MediaTypes.getDefault().filter("archive");
|
||||
public static final ExtensionFileFilter SFV_FILES = MediaTypes.getDefault().filter("verification/sfv");
|
||||
|
||||
|
||||
/**
|
||||
* Dummy constructor to prevent instantiation.
|
||||
*/
|
||||
|
@ -18,6 +18,9 @@ import javax.swing.JFrame;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
import net.sourceforge.filebot.ui.MainFrame;
|
||||
@ -25,9 +28,6 @@ import net.sourceforge.filebot.ui.NotificationLoggingHandler;
|
||||
import net.sourceforge.filebot.ui.SinglePanelFrame;
|
||||
import net.sourceforge.filebot.ui.panel.sfv.SfvPanelBuilder;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
|
||||
|
||||
public class Main {
|
||||
|
||||
@ -108,6 +108,7 @@ public class Main {
|
||||
*/
|
||||
private static void initializeSettings() {
|
||||
Settings.userRoot().putDefault("thetvdb.apikey", "58B4AA94C59AD656");
|
||||
Settings.userRoot().putDefault("sublight.apikey", "afa9ecb2-a3ee-42b1-9225-000b4038bc85");
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,7 +4,6 @@ package net.sourceforge.filebot;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -24,8 +23,9 @@ public class MediaTypes {
|
||||
@XmlElement(name = "type")
|
||||
private Type[] types;
|
||||
|
||||
|
||||
|
||||
private static class Type {
|
||||
|
||||
@XmlAttribute(name = "name")
|
||||
private String name;
|
||||
|
||||
@ -33,9 +33,10 @@ public class MediaTypes {
|
||||
private String[] extensions;
|
||||
}
|
||||
|
||||
|
||||
private static MediaTypes instance;
|
||||
|
||||
|
||||
|
||||
public static synchronized MediaTypes getDefault() {
|
||||
if (instance == null) {
|
||||
try {
|
||||
@ -52,7 +53,7 @@ public class MediaTypes {
|
||||
}
|
||||
|
||||
|
||||
public FileFilter filter(String name) {
|
||||
public ExtensionFileFilter filter(String name) {
|
||||
return new ExtensionFileFilter(extensions(name));
|
||||
}
|
||||
|
||||
|
@ -33,12 +33,12 @@ import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.SimpleScriptContext;
|
||||
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
|
||||
import org.mozilla.javascript.EcmaError;
|
||||
|
||||
import com.sun.phobos.script.javascript.RhinoScriptEngine;
|
||||
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
|
||||
|
||||
public class ExpressionFormat extends Format {
|
||||
|
||||
@ -48,7 +48,7 @@ public class ExpressionFormat extends Format {
|
||||
|
||||
private ScriptException lastException;
|
||||
|
||||
|
||||
|
||||
public ExpressionFormat(String expression) throws ScriptException {
|
||||
this.expression = expression;
|
||||
this.compilation = secure(compile(expression, (Compilable) initScriptEngine()));
|
||||
@ -180,13 +180,13 @@ public class ExpressionFormat extends Format {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class PrivilegedBindings implements InvocationHandler {
|
||||
|
||||
private final Bindings bindings;
|
||||
private final AccessControlContext context;
|
||||
|
||||
|
||||
|
||||
private PrivilegedBindings(Bindings bindings, AccessControlContext context) {
|
||||
this.bindings = bindings;
|
||||
this.context = context;
|
||||
@ -232,7 +232,7 @@ public class ExpressionFormat extends Format {
|
||||
private final CompiledScript compiledScript;
|
||||
private final AccessControlContext sandbox;
|
||||
|
||||
|
||||
|
||||
private SecureCompiledScript(CompiledScript compiledScript, AccessControlContext sandbox) {
|
||||
this.compiledScript = compiledScript;
|
||||
this.sandbox = sandbox;
|
||||
@ -271,7 +271,7 @@ public class ExpressionFormat extends Format {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Object parseObject(String source, ParsePosition pos) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -31,6 +31,18 @@
|
||||
</type>
|
||||
|
||||
|
||||
<!--
|
||||
Archive
|
||||
-->
|
||||
<type name="archive/zip">
|
||||
<extension>zip</extension>
|
||||
</type>
|
||||
|
||||
<type name="archive/rar">
|
||||
<extension>rar</extension>
|
||||
</type>
|
||||
|
||||
|
||||
<!--
|
||||
Audio
|
||||
-->
|
||||
@ -42,20 +54,20 @@
|
||||
<extension>mp4</extension>
|
||||
<extension>m4a</extension>
|
||||
</type>
|
||||
|
||||
|
||||
<type name="audio/flac">
|
||||
<extension>flac</extension>
|
||||
</type>
|
||||
|
||||
|
||||
<type name="audio/wma">
|
||||
<extension>wma</extension>
|
||||
</type>
|
||||
|
||||
|
||||
<type name="audio/ogg">
|
||||
<extension>ogg</extension>
|
||||
</type>
|
||||
|
||||
|
||||
</type>
|
||||
|
||||
|
||||
<!--
|
||||
Video
|
||||
-->
|
||||
@ -88,6 +100,10 @@
|
||||
<extension>wmv</extension>
|
||||
</type>
|
||||
|
||||
<type name="video/flash">
|
||||
<extension>flv</extension>
|
||||
</type>
|
||||
|
||||
|
||||
<!--
|
||||
Subtitles
|
||||
|
BIN
source/net/sourceforge/filebot/resources/bullet.green.png
Normal file
BIN
source/net/sourceforge/filebot/resources/bullet.green.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 295 B |
BIN
source/net/sourceforge/filebot/resources/bullet.yellow.png
Normal file
BIN
source/net/sourceforge/filebot/resources/bullet.yellow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 287 B |
BIN
source/net/sourceforge/filebot/resources/package.extract.png
Normal file
BIN
source/net/sourceforge/filebot/resources/package.extract.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 781 B |
BIN
source/net/sourceforge/filebot/resources/package.fetch.png
Normal file
BIN
source/net/sourceforge/filebot/resources/package.fetch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 759 B |
@ -12,7 +12,7 @@ public class SeasonEpisodeMatcher {
|
||||
|
||||
private final SeasonEpisodePattern[] patterns;
|
||||
|
||||
|
||||
|
||||
public SeasonEpisodeMatcher() {
|
||||
patterns = new SeasonEpisodePattern[3];
|
||||
|
||||
@ -61,7 +61,7 @@ public class SeasonEpisodeMatcher {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class SxE {
|
||||
|
||||
public static final int UNDEFINED = -1;
|
||||
@ -69,7 +69,7 @@ public class SeasonEpisodeMatcher {
|
||||
public final int season;
|
||||
public final int episode;
|
||||
|
||||
|
||||
|
||||
public SxE(int season, int episode) {
|
||||
this.season = season;
|
||||
this.episode = episode;
|
||||
@ -102,6 +102,12 @@ public class SeasonEpisodeMatcher {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return season ^ episode;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%dx%02d", season, episode);
|
||||
@ -116,7 +122,7 @@ public class SeasonEpisodeMatcher {
|
||||
protected final int seasonGroup;
|
||||
protected final int episodeGroup;
|
||||
|
||||
|
||||
|
||||
public SeasonEpisodePattern(String pattern) {
|
||||
this(Pattern.compile(pattern), 1, 2);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import java.awt.event.MouseEvent;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
@ -34,7 +35,7 @@ public class SelectDialog<T> extends JDialog {
|
||||
|
||||
private boolean valueSelected = false;
|
||||
|
||||
|
||||
|
||||
public SelectDialog(Window owner, Collection<? extends T> options) {
|
||||
super(owner, "Select", ModalityType.DOCUMENT_MODAL);
|
||||
|
||||
@ -47,7 +48,10 @@ public class SelectDialog<T> extends JDialog {
|
||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
list.setSelectedIndex(0);
|
||||
|
||||
list.setCellRenderer(new SelectListCellRenderer());
|
||||
DefaultFancyListCellRenderer renderer = new DefaultFancyListCellRenderer(4);
|
||||
renderer.setHighlightingEnabled(false);
|
||||
|
||||
list.setCellRenderer(renderer);
|
||||
list.addMouseListener(mouseListener);
|
||||
|
||||
JComponent c = (JComponent) getContentPane();
|
||||
@ -93,7 +97,8 @@ public class SelectDialog<T> extends JDialog {
|
||||
dispose();
|
||||
}
|
||||
|
||||
private AbstractAction selectAction = new AbstractAction("Select", ResourceManager.getIcon("dialog.continue")) {
|
||||
|
||||
private final Action selectAction = new AbstractAction("Select", ResourceManager.getIcon("dialog.continue")) {
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
valueSelected = true;
|
||||
@ -101,7 +106,7 @@ public class SelectDialog<T> extends JDialog {
|
||||
}
|
||||
};
|
||||
|
||||
private AbstractAction cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
|
||||
private final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
valueSelected = false;
|
||||
@ -109,7 +114,7 @@ public class SelectDialog<T> extends JDialog {
|
||||
}
|
||||
};
|
||||
|
||||
private MouseAdapter mouseListener = new MouseAdapter() {
|
||||
private final MouseAdapter mouseListener = new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
@ -119,19 +124,4 @@ public class SelectDialog<T> extends JDialog {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
protected class SelectListCellRenderer extends DefaultFancyListCellRenderer {
|
||||
|
||||
public SelectListCellRenderer() {
|
||||
super(4);
|
||||
setHighlightingEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
super.configureListCellRendererComponent(list, convertValueToString(value), index, isSelected, cellHasFocus);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class FileTreePanel extends JComponent {
|
||||
|
||||
private FileTreeTransferablePolicy transferablePolicy = new FileTreeTransferablePolicy(fileTree);
|
||||
|
||||
|
||||
|
||||
public FileTreePanel() {
|
||||
fileTree.setTransferHandler(new DefaultTransferHandler(transferablePolicy, null));
|
||||
|
||||
@ -40,7 +40,16 @@ class FileTreePanel extends JComponent {
|
||||
add(new JButton(loadAction));
|
||||
add(new JButton(clearAction), "gap 1.2mm, wrap 1.2mm");
|
||||
|
||||
TunedUtilities.syncPropertyChangeEvents(boolean.class, LOADING_PROPERTY, transferablePolicy, this);
|
||||
// forward loading events
|
||||
transferablePolicy.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (LOADING_PROPERTY.equals(evt.getPropertyName())) {
|
||||
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// update tree when loading is finished
|
||||
transferablePolicy.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@ -66,6 +75,7 @@ class FileTreePanel extends JComponent {
|
||||
return transferablePolicy;
|
||||
}
|
||||
|
||||
|
||||
private final LoadAction loadAction = new LoadAction(transferablePolicy);
|
||||
|
||||
private final AbstractAction clearAction = new AbstractAction("Clear", ResourceManager.getIcon("action.clear")) {
|
||||
@ -98,7 +108,7 @@ class FileTreePanel extends JComponent {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
private void fireFileTreeChange() {
|
||||
firePropertyChange("filetree", null, fileTree);
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class ListPanel extends JComponent {
|
||||
private SpinnerNumberModel fromSpinnerModel = new SpinnerNumberModel(1, 0, Integer.MAX_VALUE, 1);
|
||||
private SpinnerNumberModel toSpinnerModel = new SpinnerNumberModel(20, 0, Integer.MAX_VALUE, 1);
|
||||
|
||||
|
||||
|
||||
public ListPanel() {
|
||||
|
||||
list.setTitle("Title");
|
||||
@ -88,6 +88,7 @@ public class ListPanel extends JComponent {
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke("ENTER"), createAction);
|
||||
}
|
||||
|
||||
|
||||
private AbstractAction createAction = new AbstractAction("Create") {
|
||||
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
@ -119,8 +120,8 @@ public class ListPanel extends JComponent {
|
||||
|
||||
// numbers
|
||||
bindings.put("index", i);
|
||||
bindings.put("min", min);
|
||||
bindings.put("max", max);
|
||||
bindings.put("from", from);
|
||||
bindings.put("to", to);
|
||||
|
||||
names.add(format.format(bindings, new StringBuffer()).toString());
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class History {
|
||||
@XmlElement(name = "sequence")
|
||||
private List<Sequence> sequences;
|
||||
|
||||
|
||||
|
||||
public History() {
|
||||
this.sequences = new ArrayList<Sequence>();
|
||||
}
|
||||
@ -37,7 +37,7 @@ class History {
|
||||
this.sequences = new ArrayList<Sequence>(sequences);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class Sequence {
|
||||
|
||||
@XmlAttribute(name = "date", required = true)
|
||||
@ -46,7 +46,7 @@ class History {
|
||||
@XmlElement(name = "rename", required = true)
|
||||
private List<Element> elements;
|
||||
|
||||
|
||||
|
||||
private Sequence() {
|
||||
// hide constructor
|
||||
}
|
||||
@ -71,6 +71,12 @@ class History {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return elements.hashCode() ^ date.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -85,7 +91,7 @@ class History {
|
||||
@XmlAttribute(name = "to", required = true)
|
||||
private String to;
|
||||
|
||||
|
||||
|
||||
private Element() {
|
||||
// hide constructor
|
||||
}
|
||||
@ -115,9 +121,15 @@ class History {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return to.hashCode() ^ from.hashCode() ^ dir.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<Sequence> sequences() {
|
||||
return unmodifiableList(sequences);
|
||||
}
|
||||
@ -186,6 +198,12 @@ class History {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return sequences.hashCode();
|
||||
}
|
||||
|
||||
|
||||
public static void exportHistory(History history, File file) throws IOException {
|
||||
try {
|
||||
Marshaller marshaller = JAXBContext.newInstance(History.class).createMarshaller();
|
||||
|
@ -2,6 +2,7 @@
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
@ -9,6 +10,6 @@ import java.util.Map;
|
||||
|
||||
interface Archive {
|
||||
|
||||
Map<String, ByteBuffer> extract() throws IOException;
|
||||
Map<File, ByteBuffer> extract() throws IOException;
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
@ -16,6 +17,7 @@ enum ArchiveType {
|
||||
return new ZipArchive(data);
|
||||
}
|
||||
},
|
||||
|
||||
RAR {
|
||||
|
||||
@Override
|
||||
@ -23,6 +25,7 @@ enum ArchiveType {
|
||||
return new RarArchive(data);
|
||||
}
|
||||
},
|
||||
|
||||
UNDEFINED {
|
||||
|
||||
@Override
|
||||
@ -31,7 +34,7 @@ enum ArchiveType {
|
||||
return new Archive() {
|
||||
|
||||
@Override
|
||||
public Map<String, ByteBuffer> extract() throws IOException {
|
||||
public Map<File, ByteBuffer> extract() throws IOException {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
};
|
||||
@ -40,13 +43,10 @@ enum ArchiveType {
|
||||
|
||||
|
||||
public static ArchiveType forName(String name) {
|
||||
if (name == null)
|
||||
return UNDEFINED;
|
||||
|
||||
if (name.equalsIgnoreCase("zip"))
|
||||
if ("zip".equalsIgnoreCase(name))
|
||||
return ZIP;
|
||||
|
||||
if (name.equalsIgnoreCase("rar"))
|
||||
if ("rar".equalsIgnoreCase(name))
|
||||
return RAR;
|
||||
|
||||
return UNDEFINED;
|
||||
@ -55,9 +55,4 @@ enum ArchiveType {
|
||||
|
||||
public abstract Archive fromData(ByteBuffer data);
|
||||
|
||||
|
||||
public String getExtension() {
|
||||
return toString().toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -25,8 +26,8 @@ class RarArchive implements Archive {
|
||||
}
|
||||
|
||||
|
||||
public Map<String, ByteBuffer> extract() throws IOException {
|
||||
Map<String, ByteBuffer> vfs = new LinkedHashMap<String, ByteBuffer>();
|
||||
public Map<File, ByteBuffer> extract() throws IOException {
|
||||
Map<File, ByteBuffer> vfs = new LinkedHashMap<File, ByteBuffer>();
|
||||
|
||||
try {
|
||||
de.innosystec.unrar.Archive rar = new de.innosystec.unrar.Archive(data.duplicate());
|
||||
@ -44,7 +45,7 @@ class RarArchive implements Archive {
|
||||
rar.extractFile(header, buffer);
|
||||
|
||||
// add memory file
|
||||
vfs.put(header.getFileNameString(), buffer.getByteBuffer());
|
||||
vfs.put(new File(header.getFileNameString()), buffer.getByteBuffer());
|
||||
} catch (OutOfMemoryError e) {
|
||||
// ignore, there seems to be bug with JUnRar allocating lots of memory for no apparent reason
|
||||
// @see https://sourceforge.net/forum/forum.php?thread_id=2773018&forum_id=706772
|
||||
|
@ -3,11 +3,11 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Insets;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
@ -20,21 +20,15 @@ public class SubtitleListCellRenderer extends AbstractFancyListCellRenderer {
|
||||
private final JLabel titleLabel = new JLabel();
|
||||
private final JLabel languageLabel = new JLabel();
|
||||
|
||||
private final JProgressBar progressBar = new JProgressBar(0, 100);
|
||||
|
||||
|
||||
public SubtitleListCellRenderer() {
|
||||
super(new Insets(5, 5, 5, 5));
|
||||
setHighlightingEnabled(false);
|
||||
|
||||
progressBar.setStringPainted(true);
|
||||
progressBar.setOpaque(false);
|
||||
progressBar.setPreferredSize(new Dimension(80, 18));
|
||||
|
||||
setLayout(new MigLayout("fill, nogrid, insets 0"));
|
||||
|
||||
add(languageLabel, "hidemode 3, w 85px!");
|
||||
add(titleLabel, "");
|
||||
add(progressBar, "gap indent:push");
|
||||
add(titleLabel);
|
||||
|
||||
setBorder(new CompoundBorder(new DashedSeparator(2, 4, Color.lightGray, Color.white), getBorder()));
|
||||
}
|
||||
@ -46,18 +40,14 @@ public class SubtitleListCellRenderer extends AbstractFancyListCellRenderer {
|
||||
|
||||
SubtitlePackage subtitle = (SubtitlePackage) value;
|
||||
|
||||
titleLabel.setIcon(ResourceManager.getIcon("status.archive"));
|
||||
titleLabel.setText(subtitle.getName());
|
||||
titleLabel.setIcon(getIcon(subtitle));
|
||||
|
||||
if (languageLabel.isVisible()) {
|
||||
languageLabel.setText(subtitle.getLanguage().getName());
|
||||
languageLabel.setIcon(ResourceManager.getFlagIcon(subtitle.getLanguage().getCode()));
|
||||
}
|
||||
|
||||
//TODO download + progress
|
||||
progressBar.setVisible(false);
|
||||
progressBar.setString(subtitle.getDownload().getState().toString().toLowerCase());
|
||||
|
||||
titleLabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
|
||||
languageLabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
|
||||
|
||||
@ -66,6 +56,23 @@ public class SubtitleListCellRenderer extends AbstractFancyListCellRenderer {
|
||||
}
|
||||
|
||||
|
||||
private Icon getIcon(SubtitlePackage subtitle) {
|
||||
switch (subtitle.getDownload().getPhase()) {
|
||||
case PENDING:
|
||||
return ResourceManager.getIcon(subtitle.getArchiveType() != ArchiveType.UNDEFINED ? "bullet.green" : "bullet.yellow");
|
||||
case DOWNLOADING:
|
||||
return ResourceManager.getIcon("package.fetch");
|
||||
case EXTRACTING:
|
||||
return ResourceManager.getIcon("package.extract");
|
||||
case DONE:
|
||||
return ResourceManager.getIcon("status.ok");
|
||||
}
|
||||
|
||||
// unreachable
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public JLabel getLanguageLabel() {
|
||||
return languageLabel;
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
@ -13,44 +15,42 @@ import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import ca.odell.glazedlists.BasicEventList;
|
||||
import ca.odell.glazedlists.EventList;
|
||||
import ca.odell.glazedlists.FilterList;
|
||||
import ca.odell.glazedlists.GlazedLists;
|
||||
import ca.odell.glazedlists.ListSelection;
|
||||
import ca.odell.glazedlists.ObservableElementList;
|
||||
import ca.odell.glazedlists.TextFilterator;
|
||||
import ca.odell.glazedlists.matchers.MatcherEditor;
|
||||
import ca.odell.glazedlists.swing.EventListModel;
|
||||
import ca.odell.glazedlists.swing.EventSelectionModel;
|
||||
import ca.odell.glazedlists.swing.TextComponentMatcherEditor;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
|
||||
|
||||
public class SubtitleListComponent extends JComponent {
|
||||
|
||||
private EventList<SubtitlePackage> model = new BasicEventList<SubtitlePackage>();
|
||||
|
||||
private EventSelectionModel<SubtitlePackage> selectionModel = new EventSelectionModel<SubtitlePackage>(model);
|
||||
|
||||
private SubtitleListCellRenderer renderer = new SubtitleListCellRenderer();
|
||||
|
||||
private JTextField filterEditor = new JTextField();
|
||||
|
||||
|
||||
public SubtitleListComponent() {
|
||||
// allow filtering by language name and subtitle name
|
||||
TextComponentMatcherEditor<SubtitlePackage> matcherEditor = new TextComponentMatcherEditor<SubtitlePackage>(filterEditor, new TextFilterator<SubtitlePackage>() {
|
||||
|
||||
@Override
|
||||
public void getFilterStrings(List<String> list, SubtitlePackage element) {
|
||||
list.add(element.getLanguage().getName());
|
||||
list.add(element.getName());
|
||||
}
|
||||
});
|
||||
|
||||
JList list = new JList(new EventListModel<SubtitlePackage>(new FilterList<SubtitlePackage>(model, matcherEditor)));
|
||||
JList list = new JList(createListModel(model));
|
||||
list.setFixedCellHeight(32);
|
||||
list.setCellRenderer(renderer);
|
||||
list.setFixedCellHeight(35);
|
||||
list.addMouseListener(mouseListener);
|
||||
|
||||
EventSelectionModel<SubtitlePackage> selectionModel = new EventSelectionModel<SubtitlePackage>(model);
|
||||
selectionModel.setSelectionMode(ListSelection.MULTIPLE_INTERVAL_SELECTION_DEFENSIVE);
|
||||
list.setSelectionModel(selectionModel);
|
||||
|
||||
@ -66,6 +66,28 @@ public class SubtitleListComponent extends JComponent {
|
||||
}
|
||||
|
||||
|
||||
protected ListModel createListModel(EventList<SubtitlePackage> source) {
|
||||
// allow filtering by language name and subtitle name
|
||||
MatcherEditor<SubtitlePackage> matcherEditor = new TextComponentMatcherEditor<SubtitlePackage>(filterEditor, new TextFilterator<SubtitlePackage>() {
|
||||
|
||||
@Override
|
||||
public void getFilterStrings(List<String> list, SubtitlePackage element) {
|
||||
list.add(element.getLanguage().getName());
|
||||
list.add(element.getName());
|
||||
}
|
||||
});
|
||||
|
||||
// filter list
|
||||
source = new FilterList<SubtitlePackage>(source, matcherEditor);
|
||||
|
||||
// listen to changes (e.g. download progress)
|
||||
source = new ObservableElementList<SubtitlePackage>(source, GlazedLists.beanConnector(SubtitlePackage.class));
|
||||
|
||||
// as list model
|
||||
return new EventListModel<SubtitlePackage>(source);
|
||||
}
|
||||
|
||||
|
||||
public EventList<SubtitlePackage> getModel() {
|
||||
return model;
|
||||
}
|
||||
@ -76,6 +98,22 @@ public class SubtitleListComponent extends JComponent {
|
||||
}
|
||||
|
||||
|
||||
private final MouseAdapter mouseListener = new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (SwingUtilities.isLeftMouseButton(e) && (e.getClickCount() == 2)) {
|
||||
JList list = (JList) e.getSource();
|
||||
|
||||
for (Object value : list.getSelectedValues()) {
|
||||
final SubtitlePackage subtitle = (SubtitlePackage) value;
|
||||
|
||||
subtitle.getDownload().execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Action clearFilterAction = new AbstractAction(null, ResourceManager.getIcon("edit.clear")) {
|
||||
|
||||
@Override
|
||||
|
@ -2,36 +2,61 @@
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.SwingPropertyChangeSupport;
|
||||
|
||||
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
||||
import net.sourceforge.tuned.FileUtilities;
|
||||
|
||||
|
||||
public class SubtitlePackage {
|
||||
|
||||
private final SubtitleDescriptor subtitleDescriptor;
|
||||
private final String name;
|
||||
|
||||
private final Language language;
|
||||
|
||||
private final SwingWorker<ByteBuffer, ?> download;
|
||||
private final ArchiveType archiveType;
|
||||
|
||||
private Download download;
|
||||
|
||||
|
||||
public SubtitlePackage(SubtitleDescriptor subtitleDescriptor) {
|
||||
this.subtitleDescriptor = subtitleDescriptor;
|
||||
public SubtitlePackage(SubtitleDescriptor descriptor) {
|
||||
name = descriptor.getName();
|
||||
language = new Language(languageCodeByName.get(descriptor.getLanguageName()), descriptor.getLanguageName());
|
||||
archiveType = ArchiveType.forName(descriptor.getArchiveType());
|
||||
download = new Download(descriptor.getDownloadFunction(), archiveType);
|
||||
|
||||
this.language = new Language(languageCodeByName.get(subtitleDescriptor.getLanguageName()), subtitleDescriptor.getLanguageName());
|
||||
this.download = subtitleDescriptor.createDownloadTask();
|
||||
// forward phase events
|
||||
download.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals("phase")) {
|
||||
pcs.firePropertyChange("download.phase", evt.getOldValue(), evt.getNewValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return subtitleDescriptor.getName();
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@ -41,18 +66,132 @@ public class SubtitlePackage {
|
||||
|
||||
|
||||
public ArchiveType getArchiveType() {
|
||||
return ArchiveType.forName(subtitleDescriptor.getArchiveType());
|
||||
return archiveType;
|
||||
}
|
||||
|
||||
|
||||
public SwingWorker<ByteBuffer, ?> getDownload() {
|
||||
public Download getDownload() {
|
||||
return download;
|
||||
}
|
||||
|
||||
|
||||
public void reset() {
|
||||
Download old = download;
|
||||
download = new Download(old.function, old.archiveType);
|
||||
|
||||
// transfer listeners
|
||||
for (PropertyChangeListener listener : old.getPropertyChangeSupport().getPropertyChangeListeners()) {
|
||||
old.removePropertyChangeListener(listener);
|
||||
download.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
pcs.firePropertyChange("download.phase", old.getPhase(), download.getPhase());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return subtitleDescriptor.getName();
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
private final PropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
|
||||
|
||||
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
pcs.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||
pcs.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
|
||||
public static class Download extends SwingWorker<Map<File, ByteBuffer>, Void> {
|
||||
|
||||
enum Phase {
|
||||
PENDING,
|
||||
DOWNLOADING,
|
||||
EXTRACTING,
|
||||
DONE
|
||||
}
|
||||
|
||||
|
||||
private final Callable<ByteBuffer> function;
|
||||
private final ArchiveType archiveType;
|
||||
|
||||
private Phase current = Phase.PENDING;
|
||||
|
||||
|
||||
private Download(Callable<ByteBuffer> function, ArchiveType archiveType) {
|
||||
this.function = function;
|
||||
this.archiveType = archiveType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Map<File, ByteBuffer> doInBackground() throws Exception {
|
||||
setPhase(Phase.DOWNLOADING);
|
||||
|
||||
// fetch archive
|
||||
ByteBuffer data = function.call();
|
||||
|
||||
setPhase(Phase.EXTRACTING);
|
||||
|
||||
// extract contents of the archive
|
||||
Map<File, ByteBuffer> vfs = extract(archiveType, data);
|
||||
|
||||
// if we can't extract files from a rar archive, it might actually be a zip file with the wrong extension
|
||||
if (vfs.isEmpty() && archiveType != ArchiveType.ZIP) {
|
||||
vfs = extract(ArchiveType.ZIP, data);
|
||||
}
|
||||
|
||||
if (vfs.isEmpty()) {
|
||||
throw new IOException("Cannot extract files from archive");
|
||||
}
|
||||
|
||||
// return file contents
|
||||
return vfs;
|
||||
}
|
||||
|
||||
|
||||
private Map<File, ByteBuffer> extract(ArchiveType archiveType, ByteBuffer data) throws IOException {
|
||||
Map<File, ByteBuffer> vfs = new LinkedHashMap<File, ByteBuffer>();
|
||||
|
||||
for (Entry<File, ByteBuffer> entry : archiveType.fromData(data).extract().entrySet()) {
|
||||
String filename = entry.getKey().getName();
|
||||
|
||||
if (SUBTITLE_FILES.accept(filename)) {
|
||||
// keep only subtitle files
|
||||
vfs.put(entry.getKey(), entry.getValue());
|
||||
} else if (ARCHIVE_FILES.accept(filename)) {
|
||||
// extract recursively if archive contains another archive
|
||||
vfs.putAll(extract(ArchiveType.forName(FileUtilities.getExtension(filename)), entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return vfs;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
setPhase(Phase.DONE);
|
||||
}
|
||||
|
||||
|
||||
private void setPhase(Phase phase) {
|
||||
Phase old = current;
|
||||
current = phase;
|
||||
|
||||
firePropertyChange("phase", old, phase);
|
||||
}
|
||||
|
||||
|
||||
public Phase getPhase() {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,12 +78,10 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||
|
||||
@Override
|
||||
protected SubtitleProvider[] createSearchEngines() {
|
||||
String clientInfo = String.format("%s v%s", getApplicationName(), getApplicationVersion());
|
||||
|
||||
return new SubtitleProvider[] {
|
||||
new OpenSubtitlesSubtitleClient(clientInfo),
|
||||
new OpenSubtitlesSubtitleClient(String.format("%s v%s", getApplicationName(), getApplicationVersion())),
|
||||
new SubsceneSubtitleClient(),
|
||||
new SublightSubtitleClient(clientInfo),
|
||||
new SublightSubtitleClient(getApplicationName(), Settings.userRoot().get("sublight.apikey")),
|
||||
new SubtitleSourceClient()
|
||||
};
|
||||
}
|
||||
|
@ -2,10 +2,9 @@
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
@ -25,8 +24,8 @@ class ZipArchive implements Archive {
|
||||
}
|
||||
|
||||
|
||||
public Map<String, ByteBuffer> extract() throws IOException {
|
||||
Map<String, ByteBuffer> vfs = new LinkedHashMap<String, ByteBuffer>();
|
||||
public Map<File, ByteBuffer> extract() throws IOException {
|
||||
Map<File, ByteBuffer> vfs = new LinkedHashMap<File, ByteBuffer>();
|
||||
|
||||
// read first zip entry
|
||||
ZipInputStream zipInputStream = new ZipInputStream(new ByteBufferInputStream(data.duplicate()));
|
||||
@ -34,14 +33,18 @@ class ZipArchive implements Archive {
|
||||
|
||||
try {
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
// ignore directory entries
|
||||
if (zipEntry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ByteBufferOutputStream buffer = new ByteBufferOutputStream((int) zipEntry.getSize());
|
||||
ReadableByteChannel fileChannel = Channels.newChannel(zipInputStream);
|
||||
|
||||
// write contents to buffer
|
||||
while (buffer.transferFrom(fileChannel) >= 0);
|
||||
buffer.transferFully(zipInputStream);
|
||||
|
||||
// add memory file
|
||||
vfs.put(zipEntry.getName(), buffer.getByteBuffer());
|
||||
vfs.put(new File(zipEntry.getName()), buffer.getByteBuffer());
|
||||
}
|
||||
} finally {
|
||||
zipInputStream.close();
|
||||
|
@ -30,16 +30,22 @@ public class MovieDescriptor extends SearchResult {
|
||||
public boolean equals(Object object) {
|
||||
if (object instanceof MovieDescriptor) {
|
||||
MovieDescriptor other = (MovieDescriptor) object;
|
||||
return getImdbId() == other.getImdbId() && getName().equals(other.getName()) && getYear() == other.getYear();
|
||||
return imdbId == other.imdbId && name.equals(other.name) && year == other.year;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode() ^ year ^ imdbId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s (%d)", getName(), getYear());
|
||||
return String.format("%s (%d)", name, year);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ public class OpenSubtitlesSubtitleClient implements SubtitleProvider {
|
||||
private final OpenSubtitlesClient client;
|
||||
|
||||
|
||||
public OpenSubtitlesSubtitleClient(String useragent) {
|
||||
this.client = new OpenSubtitlesClient(useragent);
|
||||
public OpenSubtitlesSubtitleClient(String clientInfo) {
|
||||
this.client = new OpenSubtitlesClient(clientInfo);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,14 +2,13 @@
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.sourceforge.tuned.DownloadTask;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
||||
/**
|
||||
@ -21,7 +20,7 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor {
|
||||
|
||||
private final Map<Property, String> properties;
|
||||
|
||||
|
||||
|
||||
public static enum Property {
|
||||
IDSubMovieFile,
|
||||
MovieHash,
|
||||
@ -71,7 +70,7 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public OpenSubtitlesSubtitleDescriptor(Map<Property, String> properties) {
|
||||
this.properties = new EnumMap<Property, String>(properties);
|
||||
}
|
||||
@ -95,12 +94,14 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor {
|
||||
|
||||
|
||||
@Override
|
||||
public DownloadTask createDownloadTask() {
|
||||
try {
|
||||
return new DownloadTask(new URL(properties.get(Property.ZipDownloadLink)));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
public Callable<ByteBuffer> getDownloadFunction() {
|
||||
return new Callable<ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public ByteBuffer call() throws Exception {
|
||||
return WebRequest.fetch(new URL(properties.get(Property.ZipDownloadLink)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,12 +2,11 @@
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
public abstract class SearchResult implements Serializable {
|
||||
public abstract class SearchResult {
|
||||
|
||||
private final String name;
|
||||
protected final String name;
|
||||
|
||||
|
||||
public SearchResult(String name) {
|
||||
|
@ -10,21 +10,23 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.xml.ws.Holder;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
|
||||
import redstone.xmlrpc.util.Base64;
|
||||
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.tuned.Timer;
|
||||
import net.sublight.webservice.ArrayOfGenre;
|
||||
import net.sublight.webservice.ArrayOfIMDB;
|
||||
import net.sublight.webservice.ArrayOfRelease;
|
||||
import net.sublight.webservice.ArrayOfString;
|
||||
import net.sublight.webservice.ArrayOfSubtitle;
|
||||
import net.sublight.webservice.ArrayOfSubtitleLanguage;
|
||||
import net.sublight.webservice.ClientInfo;
|
||||
import net.sublight.webservice.Genre;
|
||||
import net.sublight.webservice.IMDB;
|
||||
import net.sublight.webservice.Release;
|
||||
@ -38,16 +40,16 @@ public class SublightSubtitleClient implements SubtitleProvider {
|
||||
|
||||
private static final String iid = "42cc1701-3752-49e2-a148-332960073452";
|
||||
|
||||
private final String clientInfo;
|
||||
private final ClientInfo clientInfo = new ClientInfo();
|
||||
|
||||
private final SubtitlesAPI2Soap webservice;
|
||||
private SubtitlesAPI2Soap webservice;
|
||||
|
||||
private String session;
|
||||
|
||||
|
||||
public SublightSubtitleClient(String clientInfo) {
|
||||
this.clientInfo = clientInfo;
|
||||
this.webservice = new SubtitlesAPI2().getSubtitlesAPI2Soap();
|
||||
public SublightSubtitleClient(String clientIdentity, String apikey) {
|
||||
clientInfo.setClientId(clientIdentity);
|
||||
clientInfo.setApiKey(apikey);
|
||||
}
|
||||
|
||||
|
||||
@ -174,31 +176,50 @@ public class SublightSubtitleClient implements SubtitleProvider {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Entry<SubtitleLanguage, String>[] aliasList = new Entry[] {
|
||||
new SimpleEntry(SubtitleLanguage.PORTUGUESE_BRAZIL, "Brazilian"),
|
||||
new SimpleEntry(SubtitleLanguage.BOSNIAN_LATIN, "Bosnian"),
|
||||
new SimpleEntry(SubtitleLanguage.SERBIAN_LATIN, "Serbian")
|
||||
};
|
||||
|
||||
|
||||
protected SubtitleLanguage getSubtitleLanguage(String languageName) {
|
||||
// check subtitle language enum
|
||||
for (SubtitleLanguage language : SubtitleLanguage.values()) {
|
||||
if (language.value().equalsIgnoreCase(languageName))
|
||||
return language;
|
||||
}
|
||||
|
||||
// special language name handling
|
||||
if (languageName.equalsIgnoreCase("Brazilian"))
|
||||
return SubtitleLanguage.PORTUGUESE_BRAZIL;
|
||||
if (languageName.equalsIgnoreCase("Bosnian"))
|
||||
return SubtitleLanguage.BOSNIAN_LATIN;
|
||||
if (languageName.equalsIgnoreCase("Serbian"))
|
||||
return SubtitleLanguage.SERBIAN_LATIN;
|
||||
// check alias list
|
||||
for (Entry<SubtitleLanguage, String> alias : aliasList) {
|
||||
if (alias.getValue().equalsIgnoreCase(languageName))
|
||||
return alias.getKey();
|
||||
}
|
||||
|
||||
// unknown language
|
||||
// illegal language name
|
||||
throw new IllegalArgumentException("Illegal language: " + languageName);
|
||||
}
|
||||
|
||||
|
||||
protected String getLanguageName(SubtitleLanguage language) {
|
||||
// check alias list
|
||||
for (Entry<SubtitleLanguage, String> alias : aliasList) {
|
||||
if (language == alias.getKey())
|
||||
return alias.getValue();
|
||||
}
|
||||
|
||||
// use language value by default
|
||||
return language.value();
|
||||
}
|
||||
|
||||
|
||||
protected byte[] getZipArchive(Subtitle subtitle) throws WebServiceException {
|
||||
// require login
|
||||
login();
|
||||
|
||||
Holder<String> ticket = new Holder<String>();
|
||||
Holder<String> data = new Holder<String>();
|
||||
Holder<byte[]> data = new Holder<byte[]>();
|
||||
Holder<String> error = new Holder<String>();
|
||||
|
||||
webservice.getDownloadTicket(session, null, subtitle.getSubtitleID(), null, ticket, null, error);
|
||||
@ -212,7 +233,7 @@ public class SublightSubtitleClient implements SubtitleProvider {
|
||||
checkError(error);
|
||||
|
||||
// return zip file bytes
|
||||
return Base64.decode(data.value.getBytes());
|
||||
return data.value;
|
||||
}
|
||||
|
||||
|
||||
@ -223,11 +244,20 @@ public class SublightSubtitleClient implements SubtitleProvider {
|
||||
|
||||
|
||||
protected synchronized void login() throws WebServiceException {
|
||||
if (webservice == null) {
|
||||
// lazy initialize because all the jax-ws class loading will take longer than a few milliseconds
|
||||
webservice = new SubtitlesAPI2().getSubtitlesAPI2Soap();
|
||||
}
|
||||
|
||||
if (session == null) {
|
||||
// args contain only iid
|
||||
ArrayOfString args = new ArrayOfString();
|
||||
args.getString().add(iid);
|
||||
|
||||
Holder<String> session = new Holder<String>();
|
||||
Holder<String> error = new Holder<String>();
|
||||
|
||||
webservice.logInAnonymous3(clientInfo, iid, session, null, error);
|
||||
webservice.logInAnonymous4(clientInfo, args, session, null, error);
|
||||
|
||||
// abort if something went wrong
|
||||
checkError(error);
|
||||
@ -261,7 +291,7 @@ public class SublightSubtitleClient implements SubtitleProvider {
|
||||
|
||||
protected void checkError(Holder<?> error) throws WebServiceException {
|
||||
if (error.value != null) {
|
||||
throw new WebServiceException("Login failed: " + error.value);
|
||||
throw new WebServiceException("Response indicates error: " + error.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@ package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
import java.util.Formatter;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import net.sublight.webservice.Subtitle;
|
||||
|
||||
@ -14,33 +14,63 @@ public class SublightSubtitleDescriptor implements SubtitleDescriptor {
|
||||
private final Subtitle subtitle;
|
||||
private final SublightSubtitleClient source;
|
||||
|
||||
private final String name;
|
||||
private final String languageName;
|
||||
|
||||
|
||||
public SublightSubtitleDescriptor(Subtitle subtitle, SublightSubtitleClient source) {
|
||||
this.subtitle = subtitle;
|
||||
this.source = source;
|
||||
|
||||
this.name = getName(subtitle);
|
||||
this.languageName = source.getLanguageName(subtitle.getLanguage());
|
||||
}
|
||||
|
||||
|
||||
private String getName(Subtitle subtitle) {
|
||||
String releaseName = subtitle.getRelease();
|
||||
|
||||
// check if release name contains sufficient information to be used as display name
|
||||
if (releaseName != null && !releaseName.isEmpty()) {
|
||||
boolean isValid = true;
|
||||
|
||||
if (subtitle.getSeason() != null) {
|
||||
isValid &= releaseName.contains(subtitle.getSeason().toString());
|
||||
}
|
||||
|
||||
if (subtitle.getEpisode() != null) {
|
||||
isValid &= releaseName.contains(subtitle.getEpisode().toString());
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
return releaseName;
|
||||
}
|
||||
}
|
||||
|
||||
// format proper display name
|
||||
Formatter builder = new Formatter(new StringBuilder(subtitle.getTitle()));
|
||||
|
||||
if (subtitle.getSeason() != null || subtitle.getEpisode() != null) {
|
||||
builder.format(" - S%02dE%02d", subtitle.getSeason(), subtitle.getEpisode());
|
||||
}
|
||||
|
||||
if (subtitle.getRelease() != null && !subtitle.getRelease().isEmpty()) {
|
||||
builder.format(" (%s)", subtitle.getRelease());
|
||||
}
|
||||
|
||||
return builder.out().toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
// use release name by default
|
||||
String releaseName = subtitle.getRelease();
|
||||
|
||||
if (releaseName == null || releaseName.isEmpty()) {
|
||||
// create name from subtitle information (name, season, episode, ...)
|
||||
String season = subtitle.getSeason() != null ? subtitle.getSeason().toString() : null;
|
||||
String episode = subtitle.getEpisode() != null ? subtitle.getEpisode().toString() : null;
|
||||
|
||||
return EpisodeFormat.getInstance().format(new Episode(subtitle.getTitle(), season, episode, null));
|
||||
}
|
||||
|
||||
return releaseName;
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getLanguageName() {
|
||||
return subtitle.getLanguage().value();
|
||||
return languageName;
|
||||
}
|
||||
|
||||
|
||||
@ -51,11 +81,11 @@ public class SublightSubtitleDescriptor implements SubtitleDescriptor {
|
||||
|
||||
|
||||
@Override
|
||||
public SwingWorker<ByteBuffer, ?> createDownloadTask() {
|
||||
return new SwingWorker<ByteBuffer, Void>() {
|
||||
public Callable<ByteBuffer> getDownloadFunction() {
|
||||
return new Callable<ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
protected ByteBuffer doInBackground() throws Exception {
|
||||
public ByteBuffer call() throws Exception {
|
||||
return ByteBuffer.wrap(source.getZipArchive(subtitle));
|
||||
}
|
||||
};
|
||||
|
@ -2,10 +2,11 @@
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import static java.util.Collections.*;
|
||||
|
||||
import net.sourceforge.tuned.DownloadTask;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
||||
public class SubsceneSubtitleDescriptor implements SubtitleDescriptor {
|
||||
@ -18,7 +19,7 @@ public class SubsceneSubtitleDescriptor implements SubtitleDescriptor {
|
||||
private final URL downloadLink;
|
||||
private final URL referer;
|
||||
|
||||
|
||||
|
||||
public SubsceneSubtitleDescriptor(String title, String language, String archiveType, URL downloadLink, URL referer) {
|
||||
this.title = title;
|
||||
this.language = language;
|
||||
@ -42,11 +43,14 @@ public class SubsceneSubtitleDescriptor implements SubtitleDescriptor {
|
||||
|
||||
|
||||
@Override
|
||||
public DownloadTask createDownloadTask() {
|
||||
DownloadTask downloadTask = new DownloadTask(downloadLink);
|
||||
downloadTask.setRequestHeaders(Collections.singletonMap("Referer", referer.toString()));
|
||||
|
||||
return downloadTask;
|
||||
public Callable<ByteBuffer> getDownloadFunction() {
|
||||
return new Callable<ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public ByteBuffer call() throws Exception {
|
||||
return WebRequest.fetch(downloadLink, singletonMap("Referer", referer.toString()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,21 +3,20 @@ package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
||||
public interface SubtitleDescriptor {
|
||||
|
||||
public String getName();
|
||||
String getName();
|
||||
|
||||
|
||||
public String getLanguageName();
|
||||
String getLanguageName();
|
||||
|
||||
|
||||
public String getArchiveType();
|
||||
String getArchiveType();
|
||||
|
||||
|
||||
public SwingWorker<ByteBuffer, ?> createDownloadTask();
|
||||
Callable<ByteBuffer> getDownloadFunction();
|
||||
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import net.sourceforge.tuned.DownloadTask;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
||||
public class SubtitleSourceSubtitleDescriptor implements SubtitleDescriptor {
|
||||
@ -18,7 +18,7 @@ public class SubtitleSourceSubtitleDescriptor implements SubtitleDescriptor {
|
||||
|
||||
private final URL downloadLink;
|
||||
|
||||
|
||||
|
||||
public SubtitleSourceSubtitleDescriptor(String releaseName, String language, String title, int season, int episode, URL downloadLink) {
|
||||
this.releaseName = releaseName;
|
||||
this.language = language;
|
||||
@ -63,8 +63,14 @@ public class SubtitleSourceSubtitleDescriptor implements SubtitleDescriptor {
|
||||
|
||||
|
||||
@Override
|
||||
public DownloadTask createDownloadTask() {
|
||||
return new DownloadTask(downloadLink);
|
||||
public Callable<ByteBuffer> getDownloadFunction() {
|
||||
return new Callable<ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public ByteBuffer call() throws Exception {
|
||||
return WebRequest.fetch(downloadLink);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,10 @@ import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
@ -25,6 +28,8 @@ import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import net.sourceforge.tuned.ByteBufferOutputStream;
|
||||
|
||||
|
||||
public final class WebRequest {
|
||||
|
||||
@ -41,6 +46,7 @@ public final class WebRequest {
|
||||
public static Reader getReader(URLConnection connection) throws IOException {
|
||||
try {
|
||||
connection.addRequestProperty("Accept-Encoding", "gzip,deflate");
|
||||
connection.addRequestProperty("Accept-Charset", "UTF-8,ISO-8859-1");
|
||||
} catch (IllegalStateException e) {
|
||||
// too bad, can't request gzipped document anymore
|
||||
}
|
||||
@ -84,6 +90,42 @@ public final class WebRequest {
|
||||
}
|
||||
|
||||
|
||||
public static ByteBuffer fetch(URL resource) throws IOException {
|
||||
return fetch(resource, null);
|
||||
}
|
||||
|
||||
|
||||
public static ByteBuffer fetch(URL url, Map<String, String> requestParameters) throws IOException {
|
||||
URLConnection connection = url.openConnection();
|
||||
|
||||
if (requestParameters != null) {
|
||||
for (Entry<String, String> parameter : requestParameters.entrySet()) {
|
||||
connection.addRequestProperty(parameter.getKey(), parameter.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
int contentLength = connection.getContentLength();
|
||||
|
||||
InputStream in = connection.getInputStream();
|
||||
ByteBufferOutputStream buffer = new ByteBufferOutputStream(contentLength >= 0 ? contentLength : 32 * 1024);
|
||||
|
||||
try {
|
||||
// read all
|
||||
buffer.transferFully(in);
|
||||
} catch (IOException e) {
|
||||
// if the content length is not known in advance an IOException (Premature EOF)
|
||||
// is always thrown after all the data has been read
|
||||
if (contentLength >= 0) {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
|
||||
return buffer.getByteBuffer();
|
||||
}
|
||||
|
||||
|
||||
private static Charset getCharset(String contentType) {
|
||||
if (contentType != null) {
|
||||
// e.g. Content-Type: text/html; charset=iso-8859-1
|
||||
|
@ -3,8 +3,10 @@ package net.sourceforge.tuned;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
|
||||
@ -14,15 +16,15 @@ public class ByteBufferOutputStream extends OutputStream {
|
||||
|
||||
private final float loadFactor;
|
||||
|
||||
|
||||
|
||||
public ByteBufferOutputStream(int initialCapacity) {
|
||||
this(initialCapacity, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
public ByteBufferOutputStream(int initialCapacity, float loadFactor) {
|
||||
if (initialCapacity <= 0)
|
||||
throw new IllegalArgumentException("initialCapacity must be greater than 0");
|
||||
if (initialCapacity < 0)
|
||||
throw new IllegalArgumentException("initialCapacity must not be negative");
|
||||
|
||||
if (loadFactor <= 0 || Float.isNaN(loadFactor))
|
||||
throw new IllegalArgumentException("loadFactor must be greater than 0");
|
||||
@ -33,33 +35,33 @@ public class ByteBufferOutputStream extends OutputStream {
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
public void write(int b) throws IOException {
|
||||
ensureCapacity(buffer.position() + 1);
|
||||
buffer.put((byte) b);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] src) throws IOException {
|
||||
public void write(byte[] src) throws IOException {
|
||||
ensureCapacity(buffer.position() + src.length);
|
||||
buffer.put(src);
|
||||
}
|
||||
|
||||
|
||||
public synchronized void write(ByteBuffer src) throws IOException {
|
||||
public void write(ByteBuffer src) throws IOException {
|
||||
ensureCapacity(buffer.position() + src.remaining());
|
||||
buffer.put(src);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] src, int offset, int length) throws IOException {
|
||||
public void write(byte[] src, int offset, int length) throws IOException {
|
||||
ensureCapacity(buffer.position() + length);
|
||||
buffer.put(src, offset, length);
|
||||
}
|
||||
|
||||
|
||||
public synchronized void ensureCapacity(int minCapacity) {
|
||||
public void ensureCapacity(int minCapacity) {
|
||||
if (minCapacity <= buffer.capacity())
|
||||
return;
|
||||
|
||||
@ -81,7 +83,7 @@ public class ByteBufferOutputStream extends OutputStream {
|
||||
}
|
||||
|
||||
|
||||
public synchronized ByteBuffer getByteBuffer() {
|
||||
public ByteBuffer getByteBuffer() {
|
||||
ByteBuffer result = buffer.duplicate();
|
||||
|
||||
// flip buffer so it can be read
|
||||
@ -91,7 +93,7 @@ public class ByteBufferOutputStream extends OutputStream {
|
||||
}
|
||||
|
||||
|
||||
public synchronized int transferFrom(ReadableByteChannel channel) throws IOException {
|
||||
public int transferFrom(ReadableByteChannel channel) throws IOException {
|
||||
// make sure buffer is not at its limit
|
||||
ensureCapacity(buffer.position() + 1);
|
||||
|
||||
@ -99,17 +101,33 @@ public class ByteBufferOutputStream extends OutputStream {
|
||||
}
|
||||
|
||||
|
||||
public synchronized int position() {
|
||||
public int transferFully(InputStream inputStream) throws IOException {
|
||||
return transferFully(Channels.newChannel(inputStream));
|
||||
}
|
||||
|
||||
|
||||
public int transferFully(ReadableByteChannel channel) throws IOException {
|
||||
int total = 0, read = 0;
|
||||
|
||||
while ((read = transferFrom(channel)) >= 0) {
|
||||
total += read;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
public int position() {
|
||||
return buffer.position();
|
||||
}
|
||||
|
||||
|
||||
public synchronized int capacity() {
|
||||
public int capacity() {
|
||||
return buffer.capacity();
|
||||
}
|
||||
|
||||
|
||||
public synchronized void rewind() {
|
||||
public void rewind() {
|
||||
buffer.rewind();
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ public final class FileUtilities {
|
||||
public static final long MEGA = KILO * 1024;
|
||||
public static final long GIGA = MEGA * 1024;
|
||||
|
||||
|
||||
|
||||
public static String formatSize(long size) {
|
||||
if (size >= MEGA)
|
||||
return String.format("%,d MB", size / MEGA);
|
||||
@ -122,6 +122,7 @@ public final class FileUtilities {
|
||||
return accepted;
|
||||
}
|
||||
|
||||
|
||||
public static final FileFilter FOLDERS = new FileFilter() {
|
||||
|
||||
@Override
|
||||
@ -138,12 +139,12 @@ public final class FileUtilities {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
public static class ExtensionFileFilter implements FileFilter {
|
||||
|
||||
private final String[] extensions;
|
||||
|
||||
|
||||
|
||||
public ExtensionFileFilter(String... extensions) {
|
||||
this.extensions = extensions;
|
||||
}
|
||||
@ -155,12 +156,17 @@ public final class FileUtilities {
|
||||
}
|
||||
|
||||
|
||||
public boolean accept(String name) {
|
||||
return hasExtension(name, extensions);
|
||||
}
|
||||
|
||||
|
||||
public String[] getExtensions() {
|
||||
return extensions.clone();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Dummy constructor to prevent instantiation.
|
||||
*/
|
||||
|
@ -124,12 +124,12 @@ public final class XPathUtilities {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static class NodeListDecorator extends AbstractList<Node> {
|
||||
|
||||
private final NodeList nodes;
|
||||
|
||||
|
||||
|
||||
public NodeListDecorator(NodeList nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
@ -14,9 +14,6 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
@ -33,14 +30,12 @@ import javax.swing.plaf.basic.BasicTableUI;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import javax.swing.undo.UndoManager;
|
||||
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
|
||||
|
||||
public final class TunedUtilities {
|
||||
|
||||
public static final Color TRANSLUCENT = new Color(255, 255, 255, 0);
|
||||
|
||||
|
||||
|
||||
public static void checkEventDispatchThread() {
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
throw new IllegalStateException("Method must be accessed from the Swing Event Dispatch Thread, but was called on Thread \"" + Thread.currentThread().getName() + "\"");
|
||||
@ -157,54 +152,6 @@ public final class TunedUtilities {
|
||||
}
|
||||
|
||||
|
||||
public static void syncPropertyChangeEvents(Class<?> propertyType, String property, Object from, Object to) {
|
||||
PropertyChangeDelegate.create(propertyType, property, from, to);
|
||||
}
|
||||
|
||||
|
||||
private static class PropertyChangeDelegate implements PropertyChangeListener {
|
||||
|
||||
private final String property;
|
||||
|
||||
private final Object target;
|
||||
private final Method firePropertyChange;
|
||||
|
||||
|
||||
public static PropertyChangeDelegate create(Class<?> propertyType, String property, Object source, Object target) {
|
||||
try {
|
||||
|
||||
PropertyChangeDelegate listener = new PropertyChangeDelegate(propertyType, property, target);
|
||||
source.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class).invoke(source, listener);
|
||||
|
||||
return listener;
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtilities.asRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected PropertyChangeDelegate(Class<?> propertyType, String property, Object target) throws SecurityException, NoSuchMethodException {
|
||||
this.property = property;
|
||||
this.target = target;
|
||||
|
||||
this.firePropertyChange = target.getClass().getMethod("firePropertyChange", String.class, propertyType, propertyType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (property.equals(evt.getPropertyName())) {
|
||||
try {
|
||||
firePropertyChange.invoke(target, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtilities.asRuntimeException(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.
|
||||
@ -216,7 +163,7 @@ public final class TunedUtilities {
|
||||
return new DragDropRowMouseInputHandler();
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected class DragDropRowMouseInputHandler extends MouseInputHandler {
|
||||
|
||||
@Override
|
||||
@ -231,7 +178,7 @@ public final class TunedUtilities {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Dummy constructor to prevent instantiation.
|
||||
*/
|
||||
|
@ -9,16 +9,16 @@ import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import net.sublight.webservice.Subtitle;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sublight.webservice.Subtitle;
|
||||
|
||||
|
||||
public class SublightSubtitleClientTest {
|
||||
|
||||
private static SublightSubtitleClient client = new SublightSubtitleClient("Test;0.0");
|
||||
private static SublightSubtitleClient client = new SublightSubtitleClient("FileBot", "afa9ecb2-a3ee-42b1-9225-000b4038bc85");
|
||||
|
||||
|
||||
@BeforeClass
|
||||
|
@ -10,14 +10,14 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import net.sourceforge.filebot.web.MovieDescriptor;
|
||||
import net.sourceforge.tuned.PreferencesMap.SerializableAdapter;
|
||||
import net.sourceforge.tuned.PreferencesMap.SimpleAdapter;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
import net.sourceforge.tuned.PreferencesMap.SerializableAdapter;
|
||||
import net.sourceforge.tuned.PreferencesMap.SimpleAdapter;
|
||||
|
||||
|
||||
public class PreferencesMapTest {
|
||||
|
||||
@ -166,14 +166,12 @@ public class PreferencesMapTest {
|
||||
|
||||
@Test
|
||||
public void serializableAdapter() {
|
||||
Map<String, MovieDescriptor> map = PreferencesMap.map(temp, new SerializableAdapter<MovieDescriptor>());
|
||||
Map<String, Episode> map = PreferencesMap.map(temp, new SerializableAdapter<Episode>());
|
||||
|
||||
MovieDescriptor movie = new MovieDescriptor("The Hitchhiker's Guide to the Galaxy", 1981, 42);
|
||||
Episode episode = new Episode("8 Simple Rules", 1, 1, "Pilot");
|
||||
|
||||
map.put("movie", movie);
|
||||
map.put("episode", episode);
|
||||
|
||||
MovieDescriptor retrieved = map.get("movie");
|
||||
|
||||
assertEquals(movie.getImdbId(), retrieved.getImdbId());
|
||||
assertEquals(episode.toString(), map.get("episode").toString());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user