Sort of added queueing/history

For #15
This commit is contained in:
4pr0n 2014-07-30 07:48:52 -07:00
parent e303945d3d
commit 433aa1e46e
3 changed files with 253 additions and 33 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ ripme.jar.update
*.swp *.swp
ripme.jar ripme.jar
rip.properties rip.properties
history.json

View File

@ -0,0 +1,111 @@
package com.rarchives.ripme.ui;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
public class History {
private List<Entry> list = new ArrayList<Entry>();
public void add(Entry entry) {
list.add(entry);
}
public void remove(Entry entry) {
list.remove(entry);
}
public void clear() {
list.clear();
}
public void fromJSON(JSONArray jsonArray) {
JSONObject json;
for (int i = 0; i < jsonArray.length(); i++) {
json = jsonArray.getJSONObject(i);
list.add(new Entry().fromJSON(json));
}
}
public void fromFile(String filename) throws IOException {
InputStream is = new FileInputStream(filename);
try {
String jsonString = IOUtils.toString(is);
JSONArray jsonArray = new JSONArray(jsonString);
fromJSON(jsonArray);
} finally {
is.close();
}
}
public void fromList(List<String> stringList) {
for (String item : stringList) {
Entry entry = new Entry();
entry.url = item;
list.add(entry);
}
}
public JSONArray toJSON() {
JSONArray jsonArray = new JSONArray();
for (Entry entry : list) {
jsonArray.put(entry.toJSON());
}
return jsonArray;
}
public List<Entry> toList() {
return list;
}
public void toFile(String filename) throws IOException {
OutputStream os = new FileOutputStream(filename);
try {
IOUtils.write(toJSON().toString(), os);
} finally {
os.close();
}
}
class Entry {
public String url = "",
title = "";
public int count = 0;
public Date startDate = new Date(),
modifiedDate = new Date();
public Entry() {
}
public Entry fromJSON(JSONObject json) {
this.url = json.getString("url");
this.title = json.getString("title");
this.count = json.getInt("count");
this.startDate = new Date(json.getLong("startDate"));
this.modifiedDate = new Date(json.getLong("modifiedDate"));
return this;
}
public JSONObject toJSON() {
JSONObject json = new JSONObject();
json.put("url", this.url);
json.put("title", this.title);
json.put("count", this.count);
json.put("startDate", this.startDate.getTime());
json.put("modifiedDate", this.modifiedDate.getTime());
return json;
}
public String toString() {
return this.url;
}
}
}

View File

@ -27,7 +27,6 @@ import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@ -51,6 +50,8 @@ import javax.swing.UIManager;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.text.BadLocationException; import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet; import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants; import javax.swing.text.StyleConstants;
@ -68,6 +69,10 @@ public class MainWindow implements Runnable, RipStatusHandler {
private static final Logger logger = Logger.getLogger(MainWindow.class); private static final Logger logger = Logger.getLogger(MainWindow.class);
private boolean isRipping = false; // Flag to indicate if we're ripping something
private History history = new History();
private static JFrame mainFrame; private static JFrame mainFrame;
private static JTextField ripTextfield; private static JTextField ripTextfield;
private static JButton ripButton, private static JButton ripButton,
@ -94,6 +99,13 @@ public class MainWindow implements Runnable, RipStatusHandler {
historyButtonClear, historyButtonClear,
historyButtonRerip; historyButtonRerip;
// Queue
private static JButton optionQueue;
private static JPanel queuePanel;
private static JList queueList;
private static DefaultListModel queueListModel;
private static JScrollPane queueListScroll;
// Configuration // Configuration
private static JButton optionConfiguration; private static JButton optionConfiguration;
private static JPanel configurationPanel; private static JPanel configurationPanel;
@ -227,7 +239,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
ImageIcon ripIcon = new ImageIcon(mainIcon); ImageIcon ripIcon = new ImageIcon(mainIcon);
ripButton = new JButton("<html><font size=\"5\"><b>Rip</b></font></html>", ripIcon); ripButton = new JButton("<html><font size=\"5\"><b>Rip</b></font></html>", ripIcon);
stopButton = new JButton("<html><font size=\"5\"><b>Stop</b></font></html>"); stopButton = new JButton("<html><font size=\"5\"><b>Stop</b></font></html>");
stopButton.setVisible(false); stopButton.setEnabled(false);
try { try {
Image stopIcon = ImageIO.read(getClass().getClassLoader().getResource("stop.png")); Image stopIcon = ImageIO.read(getClass().getClassLoader().getResource("stop.png"));
stopButton.setIcon(new ImageIcon(stopIcon)); stopButton.setIcon(new ImageIcon(stopIcon));
@ -238,7 +250,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
gbc.gridx = 0; ripPanel.add(new JLabel("URL:", JLabel.RIGHT), gbc); gbc.gridx = 0; ripPanel.add(new JLabel("URL:", JLabel.RIGHT), gbc);
gbc.gridx = 1; ripPanel.add(ripTextfield, gbc); gbc.gridx = 1; ripPanel.add(ripTextfield, gbc);
gbc.gridx = 2; ripPanel.add(ripButton, gbc); gbc.gridx = 2; ripPanel.add(ripButton, gbc);
ripPanel.add(stopButton, gbc); gbc.gridx = 3; ripPanel.add(stopButton, gbc);
statusLabel = new JLabel("Inactive"); statusLabel = new JLabel("Inactive");
statusLabel.setHorizontalAlignment(JLabel.CENTER); statusLabel.setHorizontalAlignment(JLabel.CENTER);
@ -260,6 +272,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
optionsPanel.setBorder(emptyBorder); optionsPanel.setBorder(emptyBorder);
optionLog = new JButton("Log"); optionLog = new JButton("Log");
optionHistory = new JButton("History"); optionHistory = new JButton("History");
optionQueue = new JButton("Queue");
optionConfiguration = new JButton("Configuration"); optionConfiguration = new JButton("Configuration");
try { try {
Image icon; Image icon;
@ -267,12 +280,15 @@ public class MainWindow implements Runnable, RipStatusHandler {
optionLog.setIcon(new ImageIcon(icon)); optionLog.setIcon(new ImageIcon(icon));
icon = ImageIO.read(getClass().getClassLoader().getResource("time.png")); icon = ImageIO.read(getClass().getClassLoader().getResource("time.png"));
optionHistory.setIcon(new ImageIcon(icon)); optionHistory.setIcon(new ImageIcon(icon));
icon = ImageIO.read(getClass().getClassLoader().getResource("list.png"));
optionQueue.setIcon(new ImageIcon(icon));
icon = ImageIO.read(getClass().getClassLoader().getResource("gear.png")); icon = ImageIO.read(getClass().getClassLoader().getResource("gear.png"));
optionConfiguration.setIcon(new ImageIcon(icon)); optionConfiguration.setIcon(new ImageIcon(icon));
} catch (Exception e) { } } catch (Exception e) { }
gbc.gridx = 0; optionsPanel.add(optionLog, gbc); gbc.gridx = 0; optionsPanel.add(optionLog, gbc);
gbc.gridx = 1; optionsPanel.add(optionHistory, gbc); gbc.gridx = 1; optionsPanel.add(optionHistory, gbc);
gbc.gridx = 2; optionsPanel.add(optionConfiguration, gbc); gbc.gridx = 2; optionsPanel.add(optionQueue, gbc);
gbc.gridx = 3; optionsPanel.add(optionConfiguration, gbc);
logPanel = new JPanel(new GridBagLayout()); logPanel = new JPanel(new GridBagLayout());
logPanel.setBorder(emptyBorder); logPanel.setBorder(emptyBorder);
@ -298,7 +314,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
gbc.gridx = 0; gbc.gridx = 0;
JPanel historyListPanel = new JPanel(new GridBagLayout()); JPanel historyListPanel = new JPanel(new GridBagLayout());
historyListPanel.add(historyListScroll, gbc); historyListPanel.add(historyListScroll, gbc);
gbc.ipady = 150; gbc.ipady = 180;
historyPanel.add(historyListPanel, gbc); historyPanel.add(historyListPanel, gbc);
gbc.ipady = 0; gbc.ipady = 0;
historyButtonPanel = new JPanel(new GridBagLayout()); historyButtonPanel = new JPanel(new GridBagLayout());
@ -310,6 +326,22 @@ public class MainWindow implements Runnable, RipStatusHandler {
gbc.gridy = 1; gbc.gridx = 0; gbc.gridy = 1; gbc.gridx = 0;
historyPanel.add(historyButtonPanel, gbc); historyPanel.add(historyButtonPanel, gbc);
queuePanel = new JPanel(new GridBagLayout());
queuePanel.setBorder(emptyBorder);
queuePanel.setVisible(false);
queuePanel.setPreferredSize(new Dimension(300, 250));
queueListModel = new DefaultListModel();
queueList = new JList(queueListModel);
queueList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
queueListScroll = new JScrollPane(queueList,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
gbc.gridx = 0;
JPanel queueListPanel = new JPanel(new GridBagLayout());
queueListPanel.add(queueListScroll, gbc);
queuePanel.add(queueListPanel, gbc);
gbc.ipady = 0;
configurationPanel = new JPanel(new GridBagLayout()); configurationPanel = new JPanel(new GridBagLayout());
configurationPanel.setBorder(emptyBorder); configurationPanel.setBorder(emptyBorder);
configurationPanel.setVisible(false); configurationPanel.setVisible(false);
@ -354,7 +386,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
} catch (Exception e) { } } catch (Exception e) { }
configSaveDirLabel.setToolTipText(configSaveDirLabel.getText()); configSaveDirLabel.setToolTipText(configSaveDirLabel.getText());
configSaveDirLabel.setHorizontalAlignment(JLabel.RIGHT); configSaveDirLabel.setHorizontalAlignment(JLabel.RIGHT);
configSaveDirButton = new JButton("Browse..."); configSaveDirButton = new JButton("Select Save Directory...");
gbc.gridy = 0; gbc.gridx = 0; configurationPanel.add(configUpdateLabel, gbc); gbc.gridy = 0; gbc.gridx = 0; configurationPanel.add(configUpdateLabel, gbc);
gbc.gridx = 1; configurationPanel.add(configUpdateButton, gbc); gbc.gridx = 1; configurationPanel.add(configUpdateButton, gbc);
gbc.gridy = 1; gbc.gridx = 0; configurationPanel.add(configAutoupdateCheckbox, gbc); gbc.gridy = 1; gbc.gridx = 0; configurationPanel.add(configAutoupdateCheckbox, gbc);
@ -380,6 +412,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
gbc.gridy = 3; pane.add(optionsPanel, gbc); gbc.gridy = 3; pane.add(optionsPanel, gbc);
gbc.gridy = 4; pane.add(logPanel, gbc); gbc.gridy = 4; pane.add(logPanel, gbc);
gbc.gridy = 5; pane.add(historyPanel, gbc); gbc.gridy = 5; pane.add(historyPanel, gbc);
gbc.gridy = 5; pane.add(queuePanel, gbc);
gbc.gridy = 5; pane.add(configurationPanel, gbc); gbc.gridy = 5; pane.add(configurationPanel, gbc);
} }
@ -400,9 +433,6 @@ public class MainWindow implements Runnable, RipStatusHandler {
update(); update();
} }
private void update() { private void update() {
if (!ripTextfield.isEnabled()) {
return;
}
try { try {
String urlText = ripTextfield.getText().trim(); String urlText = ripTextfield.getText().trim();
if (!urlText.startsWith("http")) { if (!urlText.startsWith("http")) {
@ -421,9 +451,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
if (ripper != null) { if (ripper != null) {
ripper.stop(); ripper.stop();
ripButton.setVisible(true); stopButton.setEnabled(false);
stopButton.setVisible(false);
ripTextfield.setEnabled(true);
statusProgress.setValue(0); statusProgress.setValue(0);
statusProgress.setVisible(false); statusProgress.setVisible(false);
pack(); pack();
@ -438,9 +466,11 @@ public class MainWindow implements Runnable, RipStatusHandler {
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
logPanel.setVisible(!logPanel.isVisible()); logPanel.setVisible(!logPanel.isVisible());
historyPanel.setVisible(false); historyPanel.setVisible(false);
queuePanel.setVisible(false);
configurationPanel.setVisible(false); configurationPanel.setVisible(false);
optionLog.setFont(optionLog.getFont().deriveFont(Font.BOLD)); optionLog.setFont(optionLog.getFont().deriveFont(Font.BOLD));
optionHistory.setFont(optionLog.getFont().deriveFont(Font.PLAIN)); optionHistory.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionQueue.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionConfiguration.setFont(optionLog.getFont().deriveFont(Font.PLAIN)); optionConfiguration.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
pack(); pack();
} }
@ -450,9 +480,25 @@ public class MainWindow implements Runnable, RipStatusHandler {
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
logPanel.setVisible(false); logPanel.setVisible(false);
historyPanel.setVisible(!historyPanel.isVisible()); historyPanel.setVisible(!historyPanel.isVisible());
queuePanel.setVisible(false);
configurationPanel.setVisible(false); configurationPanel.setVisible(false);
optionLog.setFont(optionLog.getFont().deriveFont(Font.PLAIN)); optionLog.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionHistory.setFont(optionLog.getFont().deriveFont(Font.BOLD)); optionHistory.setFont(optionLog.getFont().deriveFont(Font.BOLD));
optionQueue.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionConfiguration.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
pack();
}
});
optionQueue.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
logPanel.setVisible(false);
historyPanel.setVisible(false);
queuePanel.setVisible(!queuePanel.isVisible());
configurationPanel.setVisible(false);
optionLog.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionHistory.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionQueue.setFont(optionLog.getFont().deriveFont(Font.BOLD));
optionConfiguration.setFont(optionLog.getFont().deriveFont(Font.PLAIN)); optionConfiguration.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
pack(); pack();
} }
@ -462,9 +508,11 @@ public class MainWindow implements Runnable, RipStatusHandler {
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
logPanel.setVisible(false); logPanel.setVisible(false);
historyPanel.setVisible(false); historyPanel.setVisible(false);
queuePanel.setVisible(false);
configurationPanel.setVisible(!configurationPanel.isVisible()); configurationPanel.setVisible(!configurationPanel.isVisible());
optionLog.setFont(optionLog.getFont().deriveFont(Font.PLAIN)); optionLog.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionHistory.setFont(optionLog.getFont().deriveFont(Font.PLAIN)); optionHistory.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionQueue.setFont(optionLog.getFont().deriveFont(Font.PLAIN));
optionConfiguration.setFont(optionLog.getFont().deriveFont(Font.BOLD)); optionConfiguration.setFont(optionLog.getFont().deriveFont(Font.BOLD));
pack(); pack();
} }
@ -586,6 +634,23 @@ public class MainWindow implements Runnable, RipStatusHandler {
Utils.configureLogger(); Utils.configureLogger();
} }
}); });
queueListModel.addListDataListener(new ListDataListener() {
@Override
public void intervalAdded(ListDataEvent arg0) {
if (queueListModel.size() > 0) {
optionQueue.setText("Queue (" + queueListModel.size() + ")");
} else {
optionQueue.setText("Queue");
}
if (!isRipping) {
ripNextAlbum();
}
}
@Override
public void contentsChanged(ListDataEvent arg0) { }
@Override
public void intervalRemoved(ListDataEvent arg0) { }
});
} }
private void setupTrayIcon() { private void setupTrayIcon() {
@ -732,13 +797,59 @@ public class MainWindow implements Runnable, RipStatusHandler {
} }
private void loadHistory() { private void loadHistory() {
for (String url : Utils.getConfigList("download.history")) { history = new History();
historyListModel.addElement(url.trim()); File historyFile = new File("history.json");
if (historyFile.exists()) {
try {
logger.info("Loading history from history.json");
history.fromFile("history.json");
} catch (IOException e) {
logger.error("Failed to load history from file history.json", e);
}
}
else {
logger.info("Loading history from configuration");
history.fromList(Utils.getConfigList("download.history"));
}
for (History.Entry entry : history.toList()) {
historyListModel.addElement(entry);
} }
} }
private void saveHistory() { private void saveHistory() {
Utils.setConfigList("download.history", Arrays.asList(historyListModel.toArray())); try {
history.toFile("history.json");
} catch (IOException e) {
logger.error("Failed to save history to file history.json", e);
}
}
private void ripNextAlbum() {
isRipping = true;
if (queueListModel.size() == 0) {
// End of queue
isRipping = false;
return;
}
String nextAlbum = (String) queueListModel.remove(0);
if (queueListModel.size() == 0) {
optionQueue.setText("Queue");
}
else {
optionQueue.setText("Queue (" + queueListModel.size() + ")");
}
Thread t = ripAlbum(nextAlbum);
if (t == null) {
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
logger.error("Interrupted while waiting to rip next album", ie);
}
ripNextAlbum();
}
else {
t.start();
}
} }
private Thread ripAlbum(String urlString) { private Thread ripAlbum(String urlString) {
@ -761,9 +872,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
error("Given URL is not valid, expecting http://website.com/page/..."); error("Given URL is not valid, expecting http://website.com/page/...");
return null; return null;
} }
ripButton.setVisible(false); stopButton.setEnabled(true);
stopButton.setVisible(true);
ripTextfield.setEnabled(false);
statusProgress.setValue(100); statusProgress.setValue(100);
openButton.setVisible(false); openButton.setVisible(false);
statusLabel.setVisible(true); statusLabel.setVisible(true);
@ -775,18 +884,14 @@ public class MainWindow implements Runnable, RipStatusHandler {
} catch (Exception e) { } catch (Exception e) {
failed = true; failed = true;
logger.error("Could not find ripper for URL " + url, e); logger.error("Could not find ripper for URL " + url, e);
error("Error: " + e.getMessage()); error(e.getMessage());
} }
if (!failed) { if (!failed) {
try { try {
mainFrame.setTitle("Ripping - RipMe v" + UpdateUtils.getThisJarVersion()); mainFrame.setTitle("Ripping - RipMe v" + UpdateUtils.getThisJarVersion());
synchronized (this) {
ripTextfield.setText(ripper.getURL().toExternalForm());
}
status("Starting rip..."); status("Starting rip...");
ripper.setObserver((RipStatusHandler) this); ripper.setObserver((RipStatusHandler) this);
Thread t = new Thread(ripper); Thread t = new Thread(ripper);
t.start();
if (configShowPopup.isSelected() && if (configShowPopup.isSelected() &&
(!mainFrame.isVisible() || !mainFrame.isActive())) { (!mainFrame.isVisible() || !mainFrame.isActive())) {
mainFrame.toFront(); mainFrame.toFront();
@ -800,9 +905,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
error("Unable to rip this URL: " + e.getMessage()); error("Unable to rip this URL: " + e.getMessage());
} }
} }
ripButton.setVisible(true); stopButton.setEnabled(false);
stopButton.setVisible(false);
ripTextfield.setEnabled(true);
statusProgress.setValue(0); statusProgress.setValue(0);
pack(); pack();
return null; return null;
@ -810,7 +913,15 @@ public class MainWindow implements Runnable, RipStatusHandler {
class RipButtonHandler implements ActionListener { class RipButtonHandler implements ActionListener {
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
ripAlbum(ripTextfield.getText()); if (!queueListModel.contains(ripTextfield.getText())) {
queueListModel.add(queueListModel.size(), ripTextfield.getText());
ripTextfield.setText("");
}
else {
if (!isRipping) {
ripNextAlbum();
}
}
} }
} }
@ -856,9 +967,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
case RIP_ERRORED: case RIP_ERRORED:
appendLog( "Error: " + (String) msg.getObject(), Color.RED); appendLog( "Error: " + (String) msg.getObject(), Color.RED);
ripButton.setVisible(true); stopButton.setEnabled(false);
stopButton.setVisible(false);
ripTextfield.setEnabled(true);
statusProgress.setValue(0); statusProgress.setValue(0);
statusProgress.setVisible(false); statusProgress.setVisible(false);
openButton.setVisible(false); openButton.setVisible(false);
@ -874,9 +983,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
Utils.playSound("camera.wav"); Utils.playSound("camera.wav");
} }
saveHistory(); saveHistory();
ripButton.setVisible(true); stopButton.setEnabled(false);
stopButton.setVisible(false);
ripTextfield.setEnabled(true);
statusProgress.setValue(0); statusProgress.setValue(0);
statusProgress.setVisible(false); statusProgress.setVisible(false);
openButton.setVisible(true); openButton.setVisible(true);
@ -901,6 +1008,7 @@ public class MainWindow implements Runnable, RipStatusHandler {
} }
}); });
pack(); pack();
ripNextAlbum();
break; break;
case COMPLETED_BYTES: case COMPLETED_BYTES:
// Update completed bytes // Update completed bytes