1
0
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:
Reinhard Pointner 2009-06-13 09:53:48 +00:00
parent b087fbc490
commit 18456f6864
41 changed files with 608 additions and 293 deletions

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.

View File

@ -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.
*/

View File

@ -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");
}

View File

@ -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));
}

View File

@ -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();

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

View File

@ -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);
}

View File

@ -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);
}
};
}

View File

@ -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);
}

View File

@ -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());
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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()
};
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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)));
}
};
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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));
}
};

View File

@ -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()));
}
};
}

View File

@ -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();
}

View File

@ -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);
}
};
}

View File

@ -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

View File

@ -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();
}

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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

View File

@ -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());
}
}