diff --git a/pom.xml b/pom.xml index cb8ca1f0..1f4c1b42 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.rarchives.ripme ripme jar - 1.0.6 + 1.0.7 ripme http://rip.rarchives.com diff --git a/src/main/java/com/rarchives/ripme/ui/ClipboardUtils.java b/src/main/java/com/rarchives/ripme/ui/ClipboardUtils.java new file mode 100644 index 00000000..1cae501b --- /dev/null +++ b/src/main/java/com/rarchives/ripme/ui/ClipboardUtils.java @@ -0,0 +1,89 @@ +package com.rarchives.ripme.ui; + +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ClipboardUtils { + private static AutoripThread autoripThread = new AutoripThread(); + + public static void setClipboardAutoRip(boolean enabled) { + if (enabled) { + autoripThread.kill(); + autoripThread = new AutoripThread(); + autoripThread.isRunning = true; + autoripThread.start(); + } else { + autoripThread.kill(); + } + } + public static boolean getClipboardAutoRip() { + return autoripThread.isRunning; + } + + public static String getClipboardString() { + try { + return (String) Toolkit + .getDefaultToolkit() + .getSystemClipboard() + .getData(DataFlavor.stringFlavor); + } catch (HeadlessException e) { + e.printStackTrace(); + } catch (UnsupportedFlavorException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} + +class AutoripThread extends Thread { + public volatile boolean isRunning = false; + Set rippedURLs = new HashSet(); + + public void run() { + isRunning = true; + try { + while (isRunning) { + // Check clipboard + String clipboard = ClipboardUtils.getClipboardString(); + if (clipboard != null) { + Pattern p = Pattern.compile( + "\\b(((ht|f)tp(s?)\\:\\/\\/|~\\/|\\/)|www.)" + + "(\\w+:\\w+@)?(([-\\w]+\\.)+(com|org|net|gov" + + "|mil|biz|info|mobi|name|aero|jobs|museum" + + "|travel|[a-z]{2}))(:[\\d]{1,5})?" + + "(((\\/([-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|\\/)+|\\?|#)?" + + "((\\?([-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?" + + "([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)" + + "(&(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?" + + "([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*" + + "(#([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?\\b"); + Matcher m = p.matcher(clipboard); + while (m.find()) { + String url = m.group(); + if (!rippedURLs.contains(url)) { + rippedURLs.add(url); + // TODO Start rip + MainWindow.ripAlbumStatic(url); + } + } + } + Thread.sleep(1000); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void kill() { + isRunning = false; + } +} diff --git a/src/main/java/com/rarchives/ripme/ui/MainWindow.java b/src/main/java/com/rarchives/ripme/ui/MainWindow.java index db427499..b7d14c39 100644 --- a/src/main/java/com/rarchives/ripme/ui/MainWindow.java +++ b/src/main/java/com/rarchives/ripme/ui/MainWindow.java @@ -1,26 +1,36 @@ package com.rarchives.ripme.ui; +import java.awt.CheckboxMenuItem; import java.awt.Color; import java.awt.Container; import java.awt.Desktop; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.SystemTray; +import java.awt.TrayIcon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; +import javax.imageio.ImageIO; import javax.swing.DefaultListModel; -import javax.swing.UIManager; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; @@ -28,6 +38,7 @@ import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.text.BadLocationException; import javax.swing.text.SimpleAttributeSet; @@ -83,13 +94,16 @@ public class MainWindow implements Runnable, RipStatusHandler { private static JButton configSaveDirButton; private static JTextField configRetriesText; - // TODO Configuration components - + private static MenuItem trayMenuAbout; + private static MenuItem trayMenuExit; + private static CheckboxMenuItem trayMenuAutorip; + + private static Image mainIcon; + public MainWindow() { mainFrame = new JFrame("RipMe v" + UpdateUtils.getThisJarVersion()); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - //mainFrame.setPreferredSize(new Dimension(400, 180)); - //mainFrame.setResizable(false); + mainFrame.setResizable(false); mainFrame.setLayout(new GridBagLayout()); createUI(mainFrame.getContentPane()); @@ -99,10 +113,12 @@ public class MainWindow implements Runnable, RipStatusHandler { Thread shutdownThread = new Thread() { @Override public void run() { - saveConfig(); + shutdownCleanup(); } }; Runtime.getRuntime().addShutdownHook(shutdownThread); + + ClipboardUtils.setClipboardAutoRip(Utils.getConfigBoolean("clipboard.autorip", false)); } public void run() { @@ -111,13 +127,15 @@ public class MainWindow implements Runnable, RipStatusHandler { mainFrame.setVisible(true); } - public void saveConfig() { - saveHistory(); + public void shutdownCleanup() { Utils.setConfigBoolean("file.overwrite", configOverwriteCheckbox.isSelected()); Utils.setConfigInteger("threads.size", Integer.parseInt(configThreadsText.getText())); Utils.setConfigInteger("download.retries", Integer.parseInt(configRetriesText.getText())); Utils.setConfigInteger("download.timeout", Integer.parseInt(configTimeoutText.getText())); + Utils.setConfigBoolean("clipboard.autorip", ClipboardUtils.getClipboardAutoRip()); + saveHistory(); Utils.saveConfig(); + ClipboardUtils.setClipboardAutoRip(false); } private void status(String text) { @@ -135,6 +153,50 @@ public class MainWindow implements Runnable, RipStatusHandler { } private void createUI(Container pane) { + // System tray + PopupMenu trayMenu = new PopupMenu(); + trayMenuAbout = new MenuItem("About " + mainFrame.getTitle()); + trayMenuAbout.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + String aboutBlurb = "
Download albums from various websites. rarchives.com"; + JOptionPane.showMessageDialog(null, + aboutBlurb, + mainFrame.getTitle(), + JOptionPane.PLAIN_MESSAGE, + new ImageIcon(mainIcon)); + } + }); + trayMenuExit = new MenuItem("Exit"); + trayMenuExit.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + System.exit(0); + } + }); + trayMenuAutorip = new CheckboxMenuItem("Clipboard Autorip"); + trayMenuAutorip.setState(ClipboardUtils.getClipboardAutoRip()); + trayMenuAutorip.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent arg0) { + ClipboardUtils.setClipboardAutoRip(trayMenuAutorip.getState()); + } + }); + trayMenu.add(trayMenuAbout); + trayMenu.addSeparator(); + trayMenu.add(trayMenuAutorip); + trayMenu.addSeparator(); + trayMenu.add(trayMenuExit); + try { + mainIcon = ImageIO.read(getClass().getClassLoader().getResource("icon.png")); + TrayIcon trayIcon = new TrayIcon(mainIcon); + trayIcon.setToolTip(mainFrame.getTitle()); + trayIcon.setPopupMenu(trayMenu); + SystemTray.getSystemTray().add(trayIcon); + } catch (Exception e) { + e.printStackTrace(); + } + EmptyBorder emptyBorder = new EmptyBorder(5, 5, 5, 5); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.BOTH; @@ -148,7 +210,8 @@ public class MainWindow implements Runnable, RipStatusHandler { } ripTextfield = new JTextField("", 20); - ripButton = new JButton("Rip"); + ImageIcon ripIcon = new ImageIcon(mainIcon.getScaledInstance(20, 20, Image.SCALE_SMOOTH)); + ripButton = new JButton("Rip", ripIcon); JPanel ripPanel = new JPanel(new GridBagLayout()); ripPanel.setBorder(emptyBorder); @@ -397,7 +460,7 @@ public class MainWindow implements Runnable, RipStatusHandler { } private Thread ripAlbum(String urlString) { - saveConfig(); + shutdownCleanup(); if (urlString.toLowerCase().startsWith("gonewild:")) { urlString = "http://gonewild.com/user/" + urlString.substring(urlString.indexOf(':') + 1); } @@ -418,23 +481,33 @@ public class MainWindow implements Runnable, RipStatusHandler { openButton.setVisible(false); statusLabel.setVisible(true); mainFrame.pack(); + AbstractRipper ripper = null; + boolean failed = false; try { - AbstractRipper ripper = AbstractRipper.getRipper(url); - ripTextfield.setText(ripper.getURL().toExternalForm()); - status("Starting rip..."); - ripper.setObserver((RipStatusHandler) this); - Thread t = new Thread(ripper); - t.start(); - return t; + ripper = AbstractRipper.getRipper(url); } catch (Exception e) { - logger.error("[!] Error while ripping: " + e.getMessage(), e); - error("Unable to rip this URL: " + e.getMessage()); - ripButton.setEnabled(true); - ripTextfield.setEnabled(true); - statusProgress.setValue(0); - mainFrame.pack(); - return null; + failed = true; + logger.error("Could not find ripper for URL " + url); + error("Could not find ripper for given URL"); } + if (!failed) { + try { + ripTextfield.setText(ripper.getURL().toExternalForm()); + status("Starting rip..."); + ripper.setObserver((RipStatusHandler) this); + Thread t = new Thread(ripper); + t.start(); + return t; + } catch (Exception e) { + logger.error("[!] Error while ripping: " + e.getMessage(), e); + error("Unable to rip this URL: " + e.getMessage()); + } + } + ripButton.setEnabled(true); + ripTextfield.setEnabled(true); + statusProgress.setValue(0); + mainFrame.pack(); + return null; } class RipButtonHandler implements ActionListener { @@ -523,4 +596,9 @@ public class MainWindow implements Runnable, RipStatusHandler { return false; } } + + public static void ripAlbumStatic(String url) { + ripTextfield.setText(url); + ripButton.doClick(); + } } \ No newline at end of file diff --git a/src/main/resources/icon.ico b/src/main/resources/icon.ico new file mode 100644 index 00000000..9ea980dd Binary files /dev/null and b/src/main/resources/icon.ico differ diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png new file mode 100644 index 00000000..59192587 Binary files /dev/null and b/src/main/resources/icon.png differ diff --git a/src/main/resources/rip.properties b/src/main/resources/rip.properties index cb3240f8..020c464f 100644 --- a/src/main/resources/rip.properties +++ b/src/main/resources/rip.properties @@ -20,3 +20,5 @@ tumblr.auth = v5kUqGQXUtmF7K0itri1DGtgTs0VQpbSEbh1jxYgj9d2Sq18F8 gw.api = gonewild twitter.max_requests = 10 + +clipboard.autorip = false \ No newline at end of file