* separated pure OpenSubtitlesClient and SubtitleClient implementation for OpenSubtitles
* improved OpenSubtitlesHasher
This commit is contained in:
parent
b9906b6a0d
commit
19b99132ad
|
@ -2,24 +2,17 @@
|
|||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.awt.Window;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
|
||||
public class FileBotUtil {
|
||||
|
||||
private FileBotUtil() {
|
||||
|
||||
// hide constructor
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +29,7 @@ public class FileBotUtil {
|
|||
* @return filename stripped of invalid characters
|
||||
*/
|
||||
public static String validateFileName(String filename) {
|
||||
// strip \, /, :, *, ?, ", <, > and |
|
||||
// strip invalid characters from filename
|
||||
return INVALID_CHARACTERS_PATTERN.matcher(filename).replaceAll("");
|
||||
}
|
||||
|
||||
|
@ -54,7 +47,12 @@ public class FileBotUtil {
|
|||
return t;
|
||||
}
|
||||
|
||||
|
||||
private static final String[] TORRENT_FILE_EXTENSIONS = { "torrent" };
|
||||
private static final String[] SFV_FILE_EXTENSIONS = { "sfv" };
|
||||
private static final String[] LIST_FILE_EXTENSIONS = { "txt", "list", "" };
|
||||
private static final String[] SUBTITLE_FILE_EXTENSIONS = { "srt", "sub", "ssa" };
|
||||
|
||||
|
||||
public static boolean containsOnlyFolders(List<File> files) {
|
||||
for (File file : files) {
|
||||
if (!file.isDirectory())
|
||||
|
@ -66,54 +64,29 @@ public class FileBotUtil {
|
|||
|
||||
|
||||
public static boolean containsOnlyTorrentFiles(List<File> files) {
|
||||
for (File file : files) {
|
||||
if (!FileFormat.hasExtension(file, "torrent"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return containsOnly(files, TORRENT_FILE_EXTENSIONS);
|
||||
}
|
||||
|
||||
|
||||
public static boolean containsOnlySfvFiles(List<File> files) {
|
||||
for (File file : files) {
|
||||
if (!FileFormat.hasExtension(file, "sfv"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return containsOnly(files, SFV_FILE_EXTENSIONS);
|
||||
}
|
||||
|
||||
|
||||
public static boolean containsOnlyListFiles(List<File> files) {
|
||||
return containsOnly(files, LIST_FILE_EXTENSIONS);
|
||||
}
|
||||
|
||||
|
||||
private static boolean containsOnly(List<File> files, String[] extensions) {
|
||||
for (File file : files) {
|
||||
if (!FileFormat.hasExtension(file, "txt", "list", ""))
|
||||
if (!FileFormat.hasExtension(file, extensions))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static void registerActionForKeystroke(JComponent component, KeyStroke keystroke, Action action) {
|
||||
Integer key = action.hashCode();
|
||||
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key);
|
||||
component.getActionMap().put(key, action);
|
||||
}
|
||||
|
||||
|
||||
public static Point getPreferredLocation(JDialog dialog) {
|
||||
Window owner = dialog.getOwner();
|
||||
|
||||
if (owner == null)
|
||||
return new Point(120, 80);
|
||||
|
||||
Point p = owner.getLocation();
|
||||
Dimension d = owner.getSize();
|
||||
|
||||
return new Point(p.x + d.width / 4, p.y + d.height / 7);
|
||||
}
|
||||
|
||||
public static final FileFilter FOLDERS_ONLY = new FileFilter() {
|
||||
|
||||
@Override
|
||||
|
@ -131,4 +104,14 @@ public class FileBotUtil {
|
|||
}
|
||||
|
||||
};
|
||||
|
||||
public static final FilenameFilter SUBTITLES_ONLY = new FilenameFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return FileFormat.hasExtension(name, SUBTITLE_FILE_EXTENSIONS);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -35,7 +35,12 @@ public class FileFormat {
|
|||
if (file.isDirectory())
|
||||
return false;
|
||||
|
||||
String extension = getExtension(file);
|
||||
return hasExtension(file.getName(), extensions);
|
||||
}
|
||||
|
||||
|
||||
public static boolean hasExtension(String filename, String... extensions) {
|
||||
String extension = getExtension(filename, false);
|
||||
|
||||
for (String ext : extensions) {
|
||||
if (ext.equalsIgnoreCase(extension))
|
||||
|
|
|
@ -19,7 +19,6 @@ import javax.swing.KeyStroke;
|
|||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtil;
|
||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||
import net.sourceforge.filebot.ui.transfer.ExportHandler;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferable;
|
||||
|
@ -32,6 +31,7 @@ import net.sourceforge.filebot.ui.transferablepolicies.NullTransferablePolicy;
|
|||
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
|
||||
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class FileBotList extends JPanel implements Saveable, TransferablePolicySupport {
|
||||
|
@ -81,7 +81,7 @@ public class FileBotList extends JPanel implements Saveable, TransferablePolicyS
|
|||
|
||||
if (enableRemoveAction) {
|
||||
// Shortcut DELETE
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@ import javax.swing.border.EmptyBorder;
|
|||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtil;
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.tuned.MessageBus;
|
||||
import net.sourceforge.tuned.MessageHandler;
|
||||
import net.sourceforge.tuned.ui.ShadowBorder;
|
||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class FileBotWindow extends JFrame implements ListSelectionListener {
|
||||
|
@ -56,7 +56,7 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
|
|||
setContentPane(contentPane);
|
||||
|
||||
// Shortcut ESC
|
||||
FileBotUtil.registerActionForKeystroke(contentPane, KeyStroke.getKeyStroke("released ESCAPE"), closeAction);
|
||||
TunedUtil.registerActionForKeystroke(contentPane, KeyStroke.getKeyStroke("released ESCAPE"), closeAction);
|
||||
|
||||
setSize(760, 615);
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import java.awt.FlowLayout;
|
|||
import java.awt.Font;
|
||||
import java.awt.GridLayout;
|
||||
import java.net.URL;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Icon;
|
||||
|
@ -16,18 +15,22 @@ import javax.swing.JPanel;
|
|||
import javax.swing.JScrollPane;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import net.sourceforge.tuned.ui.HyperlinkLabel;
|
||||
|
||||
|
||||
public class HistoryPanel extends JPanel {
|
||||
|
||||
private JPanel grid = new JPanel(new GridLayout(0, 3, 15, 10));
|
||||
private final JPanel grid = new JPanel(new GridLayout(0, 3, 15, 10));
|
||||
|
||||
private final JLabel columnHeader1 = new JLabel();
|
||||
private final JLabel columnHeader2 = new JLabel();
|
||||
private final JLabel columnHeader3 = new JLabel();
|
||||
|
||||
|
||||
public HistoryPanel(String titleHeader, String infoHeader) {
|
||||
setLayout(new FlowLayout(FlowLayout.CENTER));
|
||||
public HistoryPanel() {
|
||||
super(new FlowLayout(FlowLayout.CENTER));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(grid, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scrollPane.setBorder(BorderFactory.createEmptyBorder());
|
||||
|
@ -38,48 +41,53 @@ public class HistoryPanel extends JPanel {
|
|||
setOpaque(true);
|
||||
grid.setOpaque(false);
|
||||
|
||||
JLabel titleLabel = new JLabel(titleHeader);
|
||||
JLabel infoLabel = new JLabel(infoHeader);
|
||||
JLabel durationLabel = new JLabel("Duration");
|
||||
Font font = columnHeader1.getFont().deriveFont(Font.BOLD);
|
||||
|
||||
Font font = titleLabel.getFont().deriveFont(Font.BOLD);
|
||||
columnHeader1.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
columnHeader2.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
columnHeader3.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
|
||||
titleLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
infoLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
durationLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
columnHeader1.setFont(font);
|
||||
columnHeader2.setFont(font);
|
||||
columnHeader3.setFont(font);
|
||||
|
||||
titleLabel.setFont(font);
|
||||
infoLabel.setFont(font);
|
||||
durationLabel.setFont(font);
|
||||
|
||||
grid.add(titleLabel);
|
||||
grid.add(infoLabel);
|
||||
grid.add(durationLabel);
|
||||
grid.add(columnHeader1);
|
||||
grid.add(columnHeader2);
|
||||
grid.add(columnHeader3);
|
||||
}
|
||||
|
||||
private final Border infoBorder = BorderFactory.createEmptyBorder(0, 0, 0, 10);
|
||||
|
||||
|
||||
public void add(String title, URL url, String info, long duration, Icon icon) {
|
||||
|
||||
String durationString = NumberFormat.getInstance().format(duration) + " ms";
|
||||
|
||||
JLabel titleLabel = (url != null) ? new HyperlinkLabel(title, url) : new JLabel(title);
|
||||
JLabel infoLabel = new JLabel(info);
|
||||
JLabel durationLabel = new JLabel(durationString);
|
||||
|
||||
infoLabel.setBorder(infoBorder);
|
||||
|
||||
titleLabel.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
infoLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
durationLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
|
||||
titleLabel.setIcon(icon);
|
||||
titleLabel.setIconTextGap(7);
|
||||
|
||||
grid.add(titleLabel);
|
||||
grid.add(infoLabel);
|
||||
grid.add(durationLabel);
|
||||
|
||||
public void setColumnHeader1(String text) {
|
||||
columnHeader1.setText(text);
|
||||
}
|
||||
|
||||
|
||||
public void setColumnHeader2(String text) {
|
||||
columnHeader2.setText(text);
|
||||
}
|
||||
|
||||
|
||||
public void setColumnHeader3(String text) {
|
||||
columnHeader3.setText(text);
|
||||
}
|
||||
|
||||
|
||||
public void add(String column1, URL url, Icon icon, String column2, String column3) {
|
||||
JLabel label1 = (url != null) ? new HyperlinkLabel(column1, url) : new JLabel(column1);
|
||||
JLabel label2 = new JLabel(column2);
|
||||
JLabel label3 = new JLabel(column3);
|
||||
|
||||
label1.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
label2.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
label3.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
|
||||
label1.setIcon(icon);
|
||||
label1.setIconTextGap(7);
|
||||
|
||||
label2.setBorder(new EmptyBorder(0, 0, 0, 10));
|
||||
|
||||
grid.add(label1);
|
||||
grid.add(label2);
|
||||
grid.add(label3);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@ import javax.swing.KeyStroke;
|
|||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtil;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class SelectDialog<T> extends JDialog {
|
||||
|
@ -75,7 +75,7 @@ public class SelectDialog<T> extends JDialog {
|
|||
// bounds and location
|
||||
setMinimumSize(new Dimension(175, 175));
|
||||
setSize(new Dimension(200, 190));
|
||||
setLocation(FileBotUtil.getPreferredLocation(this));
|
||||
setLocation(TunedUtil.getPreferredLocation(this));
|
||||
|
||||
// default selection
|
||||
list.setModel(new SimpleListModel(options));
|
||||
|
|
|
@ -14,10 +14,10 @@ import javax.swing.JScrollPane;
|
|||
import javax.swing.KeyStroke;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtil;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.transfer.LoadAction;
|
||||
import net.sourceforge.tuned.ui.LoadingOverlayPane;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
class FileTreePanel extends JPanel {
|
||||
|
@ -39,7 +39,7 @@ class FileTreePanel extends JPanel {
|
|||
buttons.add(Box.createGlue());
|
||||
|
||||
// Shortcut DELETE
|
||||
FileBotUtil.registerActionForKeystroke(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
TunedUtil.registerActionForKeystroke(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
|
||||
add(new LoadingOverlayPane(new JScrollPane(fileTree), ResourceManager.getIcon("loading")), BorderLayout.CENTER);
|
||||
add(buttons, BorderLayout.SOUTH);
|
||||
|
|
|
@ -21,7 +21,6 @@ import javax.swing.KeyStroke;
|
|||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtil;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.FileBotList;
|
||||
import net.sourceforge.filebot.ui.FileBotPanel;
|
||||
|
@ -30,6 +29,7 @@ import net.sourceforge.filebot.ui.MessageManager;
|
|||
import net.sourceforge.filebot.ui.transfer.LoadAction;
|
||||
import net.sourceforge.filebot.ui.transfer.SaveAction;
|
||||
import net.sourceforge.tuned.MessageBus;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class ListPanel extends FileBotPanel {
|
||||
|
@ -88,7 +88,7 @@ public class ListPanel extends FileBotPanel {
|
|||
add(spinners, BorderLayout.NORTH);
|
||||
add(list, BorderLayout.CENTER);
|
||||
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), createAction);
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), createAction);
|
||||
|
||||
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(getPanelName(), list));
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ import java.awt.event.MouseEvent;
|
|||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultListSelectionModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ListDataEvent;
|
||||
|
@ -53,7 +55,11 @@ public class RenamePanel extends FileBotPanel {
|
|||
JList list1 = namesList.getListComponent();
|
||||
JList list2 = filesList.getListComponent();
|
||||
|
||||
new SelectionSynchronizer(list1, list2);
|
||||
ListSelectionModel selectionModel = new DefaultListSelectionModel();
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
namesList.getListComponent().setSelectionModel(selectionModel);
|
||||
filesList.getListComponent().setSelectionModel(selectionModel);
|
||||
|
||||
viewPortSynchroniser = new ViewPortSynchronizer((JViewport) list1.getParent(), (JViewport) list2.getParent());
|
||||
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import javax.swing.JList;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
|
||||
class SelectionSynchronizer {
|
||||
|
||||
private final JList list1;
|
||||
private final JList list2;
|
||||
|
||||
private final SelectionSynchronizeListener selectionSynchronizeListener1;
|
||||
private final SelectionSynchronizeListener selectionSynchronizeListener2;
|
||||
|
||||
|
||||
public SelectionSynchronizer(JList list1, JList list2) {
|
||||
this.list1 = list1;
|
||||
this.list2 = list2;
|
||||
|
||||
selectionSynchronizeListener1 = new SelectionSynchronizeListener(list2);
|
||||
selectionSynchronizeListener2 = new SelectionSynchronizeListener(list1);
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
// remove listeners to avoid adding them multiple times
|
||||
list1.removeListSelectionListener(selectionSynchronizeListener1);
|
||||
list2.removeListSelectionListener(selectionSynchronizeListener2);
|
||||
|
||||
// if enabled add them again
|
||||
if (enabled) {
|
||||
list1.addListSelectionListener(selectionSynchronizeListener1);
|
||||
list2.addListSelectionListener(selectionSynchronizeListener2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class SelectionSynchronizeListener implements ListSelectionListener {
|
||||
|
||||
private JList target;
|
||||
|
||||
|
||||
public SelectionSynchronizeListener(JList target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
JList source = (JList) e.getSource();
|
||||
int index = source.getSelectedIndex();
|
||||
|
||||
if (target.getModel().getSize() > index) {
|
||||
if (index != target.getSelectedIndex()) {
|
||||
target.setSelectedIndex(index);
|
||||
}
|
||||
|
||||
target.ensureIndexIsVisible(index);
|
||||
} else {
|
||||
target.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ import net.sourceforge.filebot.FileBotUtil;
|
|||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class ValidateNamesDialog extends JDialog {
|
||||
|
@ -83,13 +84,12 @@ public class ValidateNamesDialog extends JDialog {
|
|||
c.add(listPanel, BorderLayout.CENTER);
|
||||
c.add(buttonBox, BorderLayout.SOUTH);
|
||||
|
||||
setLocation(FileBotUtil.getPreferredLocation(this));
|
||||
setLocation(TunedUtil.getPreferredLocation(this));
|
||||
|
||||
setPreferredSize(new Dimension(365, 280));
|
||||
pack();
|
||||
|
||||
// Shortcut Escape
|
||||
FileBotUtil.registerActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
||||
TunedUtil.registerActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,19 +9,25 @@ import net.sourceforge.filebot.FileFormat;
|
|||
|
||||
public class FileEntry extends AbstractFileEntry<File> {
|
||||
|
||||
private final long length;
|
||||
private final String type;
|
||||
|
||||
|
||||
public FileEntry(File file) {
|
||||
super(FileFormat.getFileName(file), file);
|
||||
|
||||
this.length = file.length();
|
||||
this.type = FileFormat.getFileType(file);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return getValue().length();
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
public String getType() {
|
||||
return FileFormat.getFileType(getValue());
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ import net.sourceforge.filebot.web.EpisodeListClient;
|
|||
|
||||
class FetchEpisodeListTask extends SwingWorker<List<Episode>, Object> {
|
||||
|
||||
private String showName;
|
||||
private EpisodeListClient searchEngine;
|
||||
private int numberOfSeason;
|
||||
private long duration;
|
||||
private final String showName;
|
||||
private final EpisodeListClient searchEngine;
|
||||
private final int numberOfSeason;
|
||||
|
||||
private long duration = -1;
|
||||
|
||||
|
||||
public FetchEpisodeListTask(EpisodeListClient searchEngine, String showname, int numberOfSeason) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.beans.PropertyChangeEvent;
|
|||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -45,13 +46,14 @@ import net.sourceforge.tuned.ui.SelectButton;
|
|||
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
|
||||
import net.sourceforge.tuned.ui.TextCompletion;
|
||||
import net.sourceforge.tuned.ui.TextFieldWithSelect;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class SearchPanel extends FileBotPanel {
|
||||
|
||||
private JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
|
||||
private HistoryPanel historyPanel = new HistoryPanel("Show", "Number of Episodes");
|
||||
private HistoryPanel historyPanel = new HistoryPanel();
|
||||
|
||||
private SpinnerNumberModel seasonSpinnerModel = new SpinnerNumberModel(SeasonSpinnerEditor.ALL_SEASONS, SeasonSpinnerEditor.ALL_SEASONS, Integer.MAX_VALUE, 1);
|
||||
|
||||
|
@ -77,6 +79,10 @@ public class SearchPanel extends FileBotPanel {
|
|||
searchFieldCompletion.addTerms(Settings.getSettings().getStringList(Settings.SEARCH_HISTORY));
|
||||
searchFieldCompletion.hook();
|
||||
|
||||
historyPanel.setColumnHeader1("Show");
|
||||
historyPanel.setColumnHeader2("Number of Episodes");
|
||||
historyPanel.setColumnHeader3("Duration");
|
||||
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
|
||||
|
||||
Box searchBox = Box.createHorizontalBox();
|
||||
|
@ -114,11 +120,9 @@ public class SearchPanel extends FileBotPanel {
|
|||
|
||||
this.add(mainPanel, BorderLayout.CENTER);
|
||||
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("UP"), upAction);
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("DOWN"), downAction);
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinClientAction(1));
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("UP"), upAction);
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("DOWN"), downAction);
|
||||
}
|
||||
|
||||
|
||||
|
@ -206,22 +210,6 @@ public class SearchPanel extends FileBotPanel {
|
|||
};
|
||||
|
||||
|
||||
private class SpinClientAction extends AbstractAction {
|
||||
|
||||
private int spin;
|
||||
|
||||
|
||||
public SpinClientAction(int spin) {
|
||||
this.spin = spin;
|
||||
}
|
||||
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
searchField.getSelectButton().spinValue(spin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class SearchTask extends SwingWorker<List<String>, Object> {
|
||||
|
||||
private String query;
|
||||
|
@ -366,7 +354,7 @@ public class SearchPanel extends FileBotPanel {
|
|||
|
||||
String info = (episodes.size() > 0) ? String.format("%d episodes", episodes.size()) : "No episodes found";
|
||||
|
||||
historyPanel.add(episodeList.getTitle(), url, info, task.getDuration(), episodeList.getIcon());
|
||||
historyPanel.add(episodeList.getTitle(), url, episodeList.getIcon(), info, NumberFormat.getInstance().format(task.getDuration()) + " ms");
|
||||
|
||||
if (episodes.size() <= 0)
|
||||
tabbedPane.remove(episodeList);
|
||||
|
|
|
@ -17,7 +17,6 @@ import javax.swing.KeyStroke;
|
|||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtil;
|
||||
import net.sourceforge.filebot.FileFormat;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.FileBotPanel;
|
||||
|
@ -26,6 +25,7 @@ import net.sourceforge.filebot.ui.SelectDialog;
|
|||
import net.sourceforge.filebot.ui.transfer.LoadAction;
|
||||
import net.sourceforge.filebot.ui.transfer.SaveAction;
|
||||
import net.sourceforge.tuned.MessageBus;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class SfvPanel extends FileBotPanel {
|
||||
|
@ -61,7 +61,7 @@ public class SfvPanel extends FileBotPanel {
|
|||
add(southPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Shortcut DELETE
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
|
||||
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(getPanelName(), sfvTable));
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.awt.BorderLayout;
|
|||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
@ -38,13 +39,14 @@ import net.sourceforge.tuned.ui.SelectButton;
|
|||
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
|
||||
import net.sourceforge.tuned.ui.TextCompletion;
|
||||
import net.sourceforge.tuned.ui.TextFieldWithSelect;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class SubtitlePanel extends FileBotPanel {
|
||||
|
||||
private JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
|
||||
private HistoryPanel historyPanel = new HistoryPanel("Show / Movie", "Number of Subtitles");
|
||||
private HistoryPanel historyPanel = new HistoryPanel();
|
||||
|
||||
private TextFieldWithSelect<SubtitleClient> searchField;
|
||||
|
||||
|
@ -67,6 +69,10 @@ public class SubtitlePanel extends FileBotPanel {
|
|||
searchFieldCompletion.addTerms(Settings.getSettings().getStringList(Settings.SUBTITLE_HISTORY));
|
||||
searchFieldCompletion.hook();
|
||||
|
||||
historyPanel.setColumnHeader1("Show / Movie");
|
||||
historyPanel.setColumnHeader2("Number of Subtitles");
|
||||
historyPanel.setColumnHeader3("Duration");
|
||||
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
|
||||
|
||||
Box searchBox = Box.createHorizontalBox();
|
||||
|
@ -99,25 +105,7 @@ public class SubtitlePanel extends FileBotPanel {
|
|||
|
||||
this.add(mainPanel, BorderLayout.CENTER);
|
||||
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
|
||||
FileBotUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinClientAction(1));
|
||||
}
|
||||
|
||||
|
||||
private class SpinClientAction extends AbstractAction {
|
||||
|
||||
private int spin;
|
||||
|
||||
|
||||
public SpinClientAction(int spin) {
|
||||
this.spin = spin;
|
||||
}
|
||||
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
searchField.getSelectButton().spinValue(spin);
|
||||
}
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||
}
|
||||
|
||||
private final AbstractAction searchAction = new AbstractAction("Find", ResourceManager.getIcon("action.find")) {
|
||||
|
@ -251,25 +239,6 @@ public class SubtitlePanel extends FileBotPanel {
|
|||
}
|
||||
|
||||
|
||||
private class FetchSubtitleListTask extends SwingWorker<List<? extends SubtitleDescriptor>, Object> {
|
||||
|
||||
private final SubtitleClient client;
|
||||
private final MovieDescriptor descriptor;
|
||||
|
||||
|
||||
public FetchSubtitleListTask(MovieDescriptor descriptor, SubtitleClient client) {
|
||||
this.descriptor = descriptor;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected List<? extends SubtitleDescriptor> doInBackground() throws Exception {
|
||||
return client.getSubtitleList(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class FetchSubtitleListTaskListener extends SwingWorkerPropertyChangeAdapter {
|
||||
|
||||
private final SubtitleListPanel subtitleSearchResultPanel;
|
||||
|
@ -295,14 +264,14 @@ public class SubtitlePanel extends FileBotPanel {
|
|||
|
||||
String info = (subtitleDescriptors.size() > 0) ? String.format("%d subtitles", subtitleDescriptors.size()) : "No subtitles found";
|
||||
|
||||
historyPanel.add(task.descriptor.toString(), null, info, 0, task.client.getIcon());
|
||||
historyPanel.add(task.getDescriptor().toString(), null, task.getClient().getIcon(), info, NumberFormat.getInstance().format(task.getDuration()) + " ms");
|
||||
|
||||
if (subtitleDescriptors.isEmpty()) {
|
||||
tabbedPane.remove(subtitleSearchResultPanel);
|
||||
return;
|
||||
}
|
||||
|
||||
tabComponent.setIcon(task.client.getIcon());
|
||||
tabComponent.setIcon(task.getClient().getIcon());
|
||||
|
||||
//TODO icon view
|
||||
//TODO sysout
|
||||
|
|
|
@ -7,15 +7,11 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
import redstone.xmlrpc.XmlRpcClient;
|
||||
import redstone.xmlrpc.XmlRpcException;
|
||||
import redstone.xmlrpc.XmlRpcFault;
|
||||
|
@ -25,18 +21,7 @@ import redstone.xmlrpc.XmlRpcFault;
|
|||
* Client for the OpenSubtitles XML-RPC API.
|
||||
*
|
||||
*/
|
||||
public class OpenSubtitlesClient extends SubtitleClient {
|
||||
|
||||
@Override
|
||||
public List<MovieDescriptor> search(String query) throws Exception {
|
||||
return searchMoviesOnIMDB(query);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<OpenSubtitleDescriptor> getSubtitleList(MovieDescriptor descriptor) throws Exception {
|
||||
return searchSubtitles(descriptor.getImdbId());
|
||||
}
|
||||
public class OpenSubtitlesClient {
|
||||
|
||||
/**
|
||||
* <table>
|
||||
|
@ -52,65 +37,57 @@ public class OpenSubtitlesClient extends SubtitleClient {
|
|||
*/
|
||||
private String url = "http://www.opensubtitles.org/xml-rpc";
|
||||
|
||||
private String username = "";
|
||||
private String password = "";
|
||||
private String language = "en";
|
||||
|
||||
private String useragent = String.format("%s v%s", Settings.NAME, Settings.VERSION);
|
||||
private String useragent;
|
||||
|
||||
private String token = null;
|
||||
|
||||
private Timer keepAliveDaemon = null;
|
||||
|
||||
/**
|
||||
* Interval to call NoOperation to keep the session from expiring
|
||||
*/
|
||||
private static final int KEEP_ALIVE_INTERVAL = 12 * 60 * 1000; // 12 minutes
|
||||
|
||||
|
||||
public OpenSubtitlesClient() {
|
||||
super("OpenSubtitles", ResourceManager.getIcon("search.opensubtitles"));
|
||||
public OpenSubtitlesClient(String useragent) {
|
||||
this.useragent = useragent;
|
||||
}
|
||||
|
||||
|
||||
public void login(String username, String password) throws XmlRpcFault {
|
||||
login(username, password, "en");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This will login user. This method should be called always when starting talking with
|
||||
* server.
|
||||
*
|
||||
* @param username blank for anonymous user.
|
||||
* @param password blank for anonymous user.
|
||||
* @param language <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes">ISO639</a>
|
||||
* 2 letter codes as language and later communication will be done in this
|
||||
* language if applicable (error codes and so on).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized void activate() throws XmlRpcFault {
|
||||
if (isActive())
|
||||
return;
|
||||
public synchronized void login(String username, String password, String language) throws XmlRpcFault {
|
||||
|
||||
Map<String, String> response = (Map<String, String>) invoke("LogIn", username, password, language, useragent);
|
||||
checkStatus(response.get("status"));
|
||||
|
||||
token = response.get("token");
|
||||
|
||||
keepAliveDaemon = new Timer(getClass().getSimpleName() + " Keepalive", true);
|
||||
keepAliveDaemon.schedule(new KeepAliveTimerTask(), KEEP_ALIVE_INTERVAL, KEEP_ALIVE_INTERVAL);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized void deactivate() {
|
||||
if (!isActive())
|
||||
return;
|
||||
public synchronized void logout() {
|
||||
|
||||
// anonymous users will always get a 401 Unauthorized when trying to logout
|
||||
if (!username.isEmpty()) {
|
||||
try {
|
||||
Map<String, String> response = (Map<String, String>) invoke("LogOut", token);
|
||||
checkStatus(response.get("status"));
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, "Exception while deactivating connection", e);
|
||||
}
|
||||
try {
|
||||
Map<String, String> response = (Map<String, String>) invoke("LogOut", token);
|
||||
checkStatus(response.get("status"));
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, "Exception while deactivating session", e);
|
||||
}
|
||||
|
||||
token = null;
|
||||
|
||||
keepAliveDaemon.cancel();
|
||||
keepAliveDaemon = null;
|
||||
}
|
||||
|
||||
|
||||
private boolean isActive() {
|
||||
public synchronized boolean isLoggedOn() {
|
||||
return token != null;
|
||||
}
|
||||
|
||||
|
@ -151,15 +128,12 @@ public class OpenSubtitlesClient extends SubtitleClient {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, String> getServerInfo() throws XmlRpcFault {
|
||||
activate();
|
||||
|
||||
return (Map<String, String>) invoke("ServerInfo", token);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<OpenSubtitleDescriptor> searchSubtitles(int... imdbidArray) throws XmlRpcFault {
|
||||
activate();
|
||||
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int... imdbidArray) throws XmlRpcFault {
|
||||
|
||||
List<Map<String, String>> imdbidList = new ArrayList<Map<String, String>>(imdbidArray.length);
|
||||
|
||||
|
@ -174,14 +148,14 @@ public class OpenSubtitlesClient extends SubtitleClient {
|
|||
|
||||
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, imdbidList);
|
||||
|
||||
ArrayList<OpenSubtitleDescriptor> subs = new ArrayList<OpenSubtitleDescriptor>();
|
||||
ArrayList<OpenSubtitlesSubtitleDescriptor> subs = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
|
||||
|
||||
if (!(response.get("data") instanceof List))
|
||||
throw new XmlRpcException("Illegal response: " + response.toString());
|
||||
|
||||
// if there was an error data may not be a list
|
||||
for (Map<String, String> subtitle : response.get("data")) {
|
||||
subs.add(new OpenSubtitleDescriptor(subtitle));
|
||||
subs.add(new OpenSubtitlesSubtitleDescriptor(subtitle));
|
||||
}
|
||||
|
||||
return subs;
|
||||
|
@ -190,7 +164,6 @@ public class OpenSubtitlesClient extends SubtitleClient {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<MovieDescriptor> searchMoviesOnIMDB(String query) throws XmlRpcFault {
|
||||
activate();
|
||||
|
||||
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchMoviesOnIMDB", token, query);
|
||||
|
||||
|
@ -207,30 +180,13 @@ public class OpenSubtitlesClient extends SubtitleClient {
|
|||
@SuppressWarnings("unchecked")
|
||||
public boolean noOperation() {
|
||||
try {
|
||||
activate();
|
||||
|
||||
Map<String, String> response = (Map<String, String>) invoke("NoOperation", token);
|
||||
checkStatus(response.get("status"));
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class KeepAliveTimerTask extends TimerTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (noOperation()) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Connection is OK");
|
||||
} else {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Connection lost");
|
||||
deactivate();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ package net.sourceforge.filebot.web;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.LongBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
|
@ -23,11 +24,6 @@ public class OpenSubtitlesHasher {
|
|||
*/
|
||||
private static final int HASH_CHUNK_SIZE = 64 * 1024;
|
||||
|
||||
/**
|
||||
* Size of the checksum in bytes (64 Bit)
|
||||
*/
|
||||
private static final int HASH_SIZE = 8;
|
||||
|
||||
|
||||
public static String computeHash(File file) throws IOException {
|
||||
long size = file.length();
|
||||
|
@ -35,63 +31,26 @@ public class OpenSubtitlesHasher {
|
|||
|
||||
FileChannel fileChannel = new FileInputStream(file).getChannel();
|
||||
|
||||
BigInteger head = computeHashForChunk(fileChannel, 0, chunkSizeForFile);
|
||||
BigInteger tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile);
|
||||
long head = computeHashForChunk(fileChannel, 0, chunkSizeForFile);
|
||||
long tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile);
|
||||
|
||||
fileChannel.close();
|
||||
|
||||
// size + head + tail
|
||||
BigInteger bigHash = BigInteger.valueOf(size).add(head.add(tail));
|
||||
|
||||
byte[] hash = getTrailingBytes(bigHash.toByteArray(), HASH_SIZE);
|
||||
|
||||
return String.format("%0" + HASH_SIZE * 2 + "x", new BigInteger(1, hash));
|
||||
return String.format("%016x", size + head + tail);
|
||||
}
|
||||
|
||||
|
||||
private static BigInteger computeHashForChunk(FileChannel fileChannel, long start, long size) throws IOException {
|
||||
MappedByteBuffer buffer = fileChannel.map(MapMode.READ_ONLY, start, size);
|
||||
private static long computeHashForChunk(FileChannel fileChannel, long start, long size) throws IOException {
|
||||
MappedByteBuffer byteBuffer = fileChannel.map(MapMode.READ_ONLY, start, size);
|
||||
|
||||
BigInteger bigHash = BigInteger.ZERO;
|
||||
byte[] bytes = new byte[HASH_SIZE];
|
||||
LongBuffer longBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asLongBuffer();
|
||||
long hash = 0;
|
||||
|
||||
while (buffer.hasRemaining()) {
|
||||
buffer.get(bytes, 0, Math.min(HASH_SIZE, buffer.remaining()));
|
||||
|
||||
// BigInteger expects a big-endian byte-order, so we reverse the byte array
|
||||
bigHash = bigHash.add(new BigInteger(1, reverse(bytes)));
|
||||
while (longBuffer.hasRemaining()) {
|
||||
hash += longBuffer.get();
|
||||
}
|
||||
|
||||
return bigHash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* copy the last n bytes to a new array
|
||||
*
|
||||
* @param bytes original array
|
||||
* @param n number of trailing bytes
|
||||
* @return new array
|
||||
*/
|
||||
private static byte[] getTrailingBytes(byte[] src, int n) {
|
||||
int length = Math.min(src.length, n);
|
||||
|
||||
byte[] dest = new byte[length];
|
||||
|
||||
int offsetSrc = Math.max(src.length - n, 0);
|
||||
System.arraycopy(src, offsetSrc, dest, 0, length);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
private static byte[] reverse(byte[] bytes) {
|
||||
byte[] reverseBytes = new byte[bytes.length];
|
||||
|
||||
for (int forward = 0, backward = bytes.length; forward < bytes.length; ++forward)
|
||||
reverseBytes[forward] = bytes[--backward];
|
||||
|
||||
return reverseBytes;
|
||||
return hash;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.resources.ResourceManager;
|
||||
|
||||
|
||||
/**
|
||||
* Client for the OpenSubtitles XML-RPC API.
|
||||
*
|
||||
*/
|
||||
public class OpenSubtitlesSubtitleClient extends SubtitleClient {
|
||||
|
||||
private final OpenSubtitlesClient client = new OpenSubtitlesClient(String.format("%s v%s", Settings.NAME, Settings.VERSION));
|
||||
|
||||
private final LogoutTimer logoutTimer = new LogoutTimer();
|
||||
|
||||
|
||||
public OpenSubtitlesSubtitleClient() {
|
||||
super("OpenSubtitles", ResourceManager.getIcon("search.opensubtitles"));
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(doLogout));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<MovieDescriptor> search(String query) throws Exception {
|
||||
activate();
|
||||
|
||||
return client.searchMoviesOnIMDB(query);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<OpenSubtitlesSubtitleDescriptor> getSubtitleList(MovieDescriptor descriptor) throws Exception {
|
||||
activate();
|
||||
|
||||
return client.searchSubtitles(descriptor.getImdbId());
|
||||
}
|
||||
|
||||
|
||||
private synchronized void activate() {
|
||||
try {
|
||||
if (!client.isLoggedOn()) {
|
||||
client.login("", "");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
|
||||
logoutTimer.restart();
|
||||
}
|
||||
|
||||
private final Runnable doLogout = new Runnable() {
|
||||
|
||||
public void run() {
|
||||
logoutTimer.stop();
|
||||
|
||||
if (client.isLoggedOn()) {
|
||||
client.logout();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private class LogoutTimer {
|
||||
|
||||
private static final int LOGOUT_DELAY = 15000; // 12 minutes
|
||||
|
||||
private Timer daemon = null;
|
||||
private LogoutTimerTask currentTimerTask = null;
|
||||
|
||||
|
||||
public synchronized void restart() {
|
||||
if (daemon == null) {
|
||||
daemon = new Timer(getClass().getName(), true);
|
||||
}
|
||||
|
||||
if (currentTimerTask != null) {
|
||||
currentTimerTask.cancel();
|
||||
daemon.purge();
|
||||
}
|
||||
|
||||
currentTimerTask = new LogoutTimerTask();
|
||||
daemon.schedule(currentTimerTask, LOGOUT_DELAY);
|
||||
}
|
||||
|
||||
|
||||
public synchronized void stop() {
|
||||
if (daemon == null)
|
||||
return;
|
||||
|
||||
currentTimerTask.cancel();
|
||||
currentTimerTask = null;
|
||||
|
||||
daemon.cancel();
|
||||
daemon = null;
|
||||
}
|
||||
|
||||
|
||||
private class LogoutTimerTask extends TimerTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
doLogout.run();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -16,7 +16,7 @@ import net.sourceforge.tuned.DownloadTask;
|
|||
*
|
||||
* @see OpenSubtitlesClient
|
||||
*/
|
||||
public class OpenSubtitleDescriptor implements SubtitleDescriptor {
|
||||
public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor {
|
||||
|
||||
private final Map<String, String> properties;
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class OpenSubtitleDescriptor implements SubtitleDescriptor {
|
|||
}
|
||||
|
||||
|
||||
public OpenSubtitleDescriptor(Map<String, String> properties) {
|
||||
public OpenSubtitlesSubtitleDescriptor(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
|
@ -25,14 +25,14 @@ import org.w3c.dom.Node;
|
|||
import org.xml.sax.SAXException;
|
||||
|
||||
|
||||
public class SubsceneClient extends SubtitleClient {
|
||||
public class SubsceneSubtitleClient extends SubtitleClient {
|
||||
|
||||
private final Map<MovieDescriptor, URL> cache = Collections.synchronizedMap(new HashMap<MovieDescriptor, URL>());
|
||||
|
||||
private final String host = "subscene.com";
|
||||
|
||||
|
||||
public SubsceneClient() {
|
||||
public SubsceneSubtitleClient() {
|
||||
super("Subscene", ResourceManager.getIcon("search.subscene"));
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@ public abstract class SubtitleClient {
|
|||
private static final List<SubtitleClient> registry = new ArrayList<SubtitleClient>();
|
||||
|
||||
static {
|
||||
registry.add(new OpenSubtitlesClient());
|
||||
registry.add(new SubsceneClient());
|
||||
registry.add(new OpenSubtitlesSubtitleClient());
|
||||
registry.add(new SubsceneSubtitleClient());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,11 @@ import java.awt.event.ActionEvent;
|
|||
import java.awt.event.ActionListener;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.border.Border;
|
||||
|
||||
import net.sourceforge.tuned.ui.SelectButton.Entry;
|
||||
|
@ -40,6 +42,9 @@ public class TextFieldWithSelect<T> extends JPanel {
|
|||
textfield.setColumns(20);
|
||||
add(textfield, BorderLayout.CENTER);
|
||||
add(selectButton, BorderLayout.WEST);
|
||||
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
|
||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinClientAction(1));
|
||||
}
|
||||
|
||||
|
||||
|
@ -71,4 +76,20 @@ public class TextFieldWithSelect<T> extends JPanel {
|
|||
|
||||
};
|
||||
|
||||
|
||||
private class SpinClientAction extends AbstractAction {
|
||||
|
||||
private int spin;
|
||||
|
||||
|
||||
public SpinClientAction(int spin) {
|
||||
this.spin = spin;
|
||||
}
|
||||
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
selectButton.spinValue(spin);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
package net.sourceforge.tuned.ui;
|
||||
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.awt.Window;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
|
||||
public class TunedUtil {
|
||||
|
||||
public static void registerActionForKeystroke(JComponent component, KeyStroke keystroke, Action action) {
|
||||
Integer key = action.hashCode();
|
||||
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key);
|
||||
component.getActionMap().put(key, action);
|
||||
}
|
||||
|
||||
|
||||
public static Point getPreferredLocation(JDialog dialog) {
|
||||
Window owner = dialog.getOwner();
|
||||
|
||||
if (owner == null)
|
||||
return new Point(120, 80);
|
||||
|
||||
Point p = owner.getLocation();
|
||||
Dimension d = owner.getSize();
|
||||
|
||||
return new Point(p.x + d.width / 4, p.y + d.height / 7);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue