Adding features to "History"
Table-based view. Tracks # of images in a rip, and dates. Sortable. Double-clicking URL allows copying the URL to clipboard. Can selectively re-rip only albums that are 'checked' 'Checked' albums are persisted when RipMe is closed & reopened. For #147 (selective re-ripping)
This commit is contained in:
parent
066b0e9f87
commit
dd55cd1b43
@ -35,7 +35,7 @@ public abstract class AlbumRipper extends AbstractRipper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return itemsCompleted.size();
|
return itemsCompleted.size() + itemsErrored.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addURLToDownload(URL url, File saveAs, String referrer, Map<String,String> cookies) {
|
public boolean addURLToDownload(URL url, File saveAs, String referrer, Map<String,String> cookies) {
|
||||||
|
@ -5,7 +5,9 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
@ -15,6 +17,13 @@ import org.json.JSONObject;
|
|||||||
public class History {
|
public class History {
|
||||||
|
|
||||||
private List<HistoryEntry> list = new ArrayList<HistoryEntry>();
|
private List<HistoryEntry> list = new ArrayList<HistoryEntry>();
|
||||||
|
private static final String[] COLUMNS = new String[] {
|
||||||
|
"URL",
|
||||||
|
"created",
|
||||||
|
"modified",
|
||||||
|
"#",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
public void add(HistoryEntry entry) {
|
public void add(HistoryEntry entry) {
|
||||||
list.add(entry);
|
list.add(entry);
|
||||||
@ -22,9 +31,60 @@ public class History {
|
|||||||
public void remove(HistoryEntry entry) {
|
public void remove(HistoryEntry entry) {
|
||||||
list.remove(entry);
|
list.remove(entry);
|
||||||
}
|
}
|
||||||
|
public void remove(int index) {
|
||||||
|
list.remove(index);
|
||||||
|
}
|
||||||
public void clear() {
|
public void clear() {
|
||||||
list.clear();
|
list.clear();
|
||||||
}
|
}
|
||||||
|
public HistoryEntry get(int index) {
|
||||||
|
return list.get(index);
|
||||||
|
}
|
||||||
|
public String getColumnName(int index) {
|
||||||
|
return COLUMNS[index];
|
||||||
|
}
|
||||||
|
public int getColumnCount() {
|
||||||
|
return COLUMNS.length;
|
||||||
|
}
|
||||||
|
public Object getValueAt(int row, int col) {
|
||||||
|
HistoryEntry entry = this.list.get(row);
|
||||||
|
switch (col) {
|
||||||
|
case 0:
|
||||||
|
return entry.url;
|
||||||
|
case 1:
|
||||||
|
return dateToHumanReadable(entry.startDate);
|
||||||
|
case 2:
|
||||||
|
return dateToHumanReadable(entry.modifiedDate);
|
||||||
|
case 3:
|
||||||
|
return entry.count;
|
||||||
|
case 4:
|
||||||
|
return entry.selected;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private String dateToHumanReadable(Date date) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
|
||||||
|
return sdf.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsURL(String url) {
|
||||||
|
for (HistoryEntry entry : this.list) {
|
||||||
|
if (entry.url.equals(url)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HistoryEntry getEntryByURL(String url) {
|
||||||
|
for (HistoryEntry entry : this.list) {
|
||||||
|
if (entry.url.equals(url)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Could not find URL " + url + " in History");
|
||||||
|
}
|
||||||
|
|
||||||
public void fromJSON(JSONArray jsonArray) {
|
public void fromJSON(JSONArray jsonArray) {
|
||||||
JSONObject json;
|
JSONObject json;
|
||||||
|
@ -43,6 +43,7 @@ import javax.swing.JOptionPane;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.JProgressBar;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JTextPane;
|
import javax.swing.JTextPane;
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
@ -53,6 +54,7 @@ import javax.swing.event.DocumentEvent;
|
|||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
import javax.swing.event.ListDataEvent;
|
import javax.swing.event.ListDataEvent;
|
||||||
import javax.swing.event.ListDataListener;
|
import javax.swing.event.ListDataListener;
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
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;
|
||||||
@ -89,10 +91,11 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
|
|
||||||
// History
|
// History
|
||||||
private static JButton optionHistory;
|
private static JButton optionHistory;
|
||||||
|
private static final History HISTORY = new History();
|
||||||
private static JPanel historyPanel;
|
private static JPanel historyPanel;
|
||||||
private static JList historyList;
|
private static JTable historyTable;
|
||||||
private static DefaultListModel historyListModel;
|
private static AbstractTableModel historyTableModel;
|
||||||
private static JScrollPane historyListScroll;
|
private static JScrollPane historyTableScrollPane;
|
||||||
private static JPanel historyButtonPanel;
|
private static JPanel historyButtonPanel;
|
||||||
private static JButton historyButtonRemove,
|
private static JButton historyButtonRemove,
|
||||||
historyButtonClear,
|
historyButtonClear,
|
||||||
@ -305,20 +308,66 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
historyPanel.setBorder(emptyBorder);
|
historyPanel.setBorder(emptyBorder);
|
||||||
historyPanel.setVisible(false);
|
historyPanel.setVisible(false);
|
||||||
historyPanel.setPreferredSize(new Dimension(300, 250));
|
historyPanel.setPreferredSize(new Dimension(300, 250));
|
||||||
historyListModel = new DefaultListModel();
|
historyTableModel = new AbstractTableModel() {
|
||||||
historyList = new JList(historyListModel);
|
private static final long serialVersionUID = 1L;
|
||||||
historyList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
@Override
|
||||||
historyListScroll = new JScrollPane(historyList,
|
public String getColumnName(int col) {
|
||||||
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
return HISTORY.getColumnName(col);
|
||||||
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
}
|
||||||
|
public Class<? extends Object> getColumnClass(int c) {
|
||||||
|
return getValueAt(0, c).getClass();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int row, int col) {
|
||||||
|
return HISTORY.getValueAt(row, col);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return HISTORY.toList().size();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return HISTORY.getColumnCount();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int row, int col) {
|
||||||
|
return (col == 0 || col == 4);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setValueAt(Object value, int row, int col) {
|
||||||
|
if (col == 4) {
|
||||||
|
HISTORY.get(row).selected = (Boolean) value;
|
||||||
|
historyTableModel.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
historyTable = new JTable(historyTableModel);
|
||||||
|
historyTable.setAutoCreateRowSorter(true);
|
||||||
|
for (int i = 0; i < historyTable.getColumnModel().getColumnCount(); i++) {
|
||||||
|
int width = 130; // Default
|
||||||
|
switch (i) {
|
||||||
|
case 0: // URL
|
||||||
|
width = 270;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
width = 40;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
width = 15;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
historyTable.getColumnModel().getColumn(i).setPreferredWidth(width);
|
||||||
|
}
|
||||||
|
historyTableScrollPane = new JScrollPane(historyTable);
|
||||||
historyButtonRemove = new JButton("Remove");
|
historyButtonRemove = new JButton("Remove");
|
||||||
historyButtonClear = new JButton("Clear");
|
historyButtonClear = new JButton("Clear");
|
||||||
historyButtonRerip = new JButton("Re-rip All");
|
historyButtonRerip = new JButton("Re-rip Checked");
|
||||||
gbc.gridx = 0;
|
gbc.gridx = 0;
|
||||||
JPanel historyListPanel = new JPanel(new GridBagLayout());
|
// History List Panel
|
||||||
historyListPanel.add(historyListScroll, gbc);
|
JPanel historyTablePanel = new JPanel(new GridBagLayout());
|
||||||
|
historyTablePanel.add(historyTableScrollPane, gbc);
|
||||||
gbc.ipady = 180;
|
gbc.ipady = 180;
|
||||||
historyPanel.add(historyListPanel, gbc);
|
historyPanel.add(historyTablePanel, gbc);
|
||||||
gbc.ipady = 0;
|
gbc.ipady = 0;
|
||||||
historyButtonPanel = new JPanel(new GridBagLayout());
|
historyButtonPanel = new JPanel(new GridBagLayout());
|
||||||
historyButtonPanel.setPreferredSize(new Dimension(300, 10));
|
historyButtonPanel.setPreferredSize(new Dimension(300, 10));
|
||||||
@ -531,17 +580,24 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
historyButtonRemove.addActionListener(new ActionListener() {
|
historyButtonRemove.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent event) {
|
public void actionPerformed(ActionEvent event) {
|
||||||
int[] indices = historyList.getSelectedIndices();
|
int[] indices = historyTable.getSelectedRows();
|
||||||
for (int i = indices.length - 1; i >= 0; i--) {
|
for (int i = indices.length - 1; i >= 0; i--) {
|
||||||
historyListModel.remove(indices[i]);
|
int modelIndex = historyTable.convertRowIndexToModel(indices[i]);
|
||||||
|
HISTORY.remove(modelIndex);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
historyTableModel.fireTableDataChanged();
|
||||||
|
} catch (Exception e) { }
|
||||||
saveHistory();
|
saveHistory();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
historyButtonClear.addActionListener(new ActionListener() {
|
historyButtonClear.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent event) {
|
public void actionPerformed(ActionEvent event) {
|
||||||
historyListModel.clear();
|
HISTORY.clear();
|
||||||
|
try {
|
||||||
|
historyTableModel.fireTableDataChanged();
|
||||||
|
} catch (Exception e) { }
|
||||||
saveHistory();
|
saveHistory();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -550,9 +606,27 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
historyButtonRerip.addActionListener(new ActionListener() {
|
historyButtonRerip.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent event) {
|
public void actionPerformed(ActionEvent event) {
|
||||||
for (int i = 0; i < historyListModel.size(); i++) {
|
if (HISTORY.toList().size() == 0) {
|
||||||
HistoryEntry entry = (HistoryEntry) historyListModel.get(i);
|
JOptionPane.showMessageDialog(null,
|
||||||
queueListModel.addElement(entry.url);
|
"There are no history entries to re-rip. Rip some albums first",
|
||||||
|
"RipMe Error",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int added = 0;
|
||||||
|
for (HistoryEntry entry : HISTORY.toList()) {
|
||||||
|
if (entry.selected) {
|
||||||
|
added++;
|
||||||
|
queueListModel.addElement(entry.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (added == 0) {
|
||||||
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"No history entries have been 'Checked'\n" +
|
||||||
|
"Check an entry by clicking the checkbox to the right of the URL",
|
||||||
|
"RipMe Error",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -794,32 +868,25 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadHistory() {
|
private void loadHistory() {
|
||||||
History history = new History();
|
|
||||||
File historyFile = new File("history.json");
|
File historyFile = new File("history.json");
|
||||||
|
HISTORY.clear();
|
||||||
if (historyFile.exists()) {
|
if (historyFile.exists()) {
|
||||||
try {
|
try {
|
||||||
logger.info("Loading history from history.json");
|
logger.info("Loading history from history.json");
|
||||||
history.fromFile("history.json");
|
HISTORY.fromFile("history.json");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed to load history from file history.json", e);
|
logger.error("Failed to load history from file " + historyFile, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.info("Loading history from configuration");
|
logger.info("Loading history from configuration");
|
||||||
history.fromList(Utils.getConfigList("download.history"));
|
HISTORY.fromList(Utils.getConfigList("download.history"));
|
||||||
}
|
|
||||||
for (HistoryEntry entry : history.toList()) {
|
|
||||||
historyListModel.addElement(entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveHistory() {
|
private void saveHistory() {
|
||||||
History history = new History();
|
|
||||||
for (int i = 0; i < historyListModel.size(); i++) {
|
|
||||||
history.add( (HistoryEntry) historyListModel.get(i) );
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
history.toFile("history.json");
|
HISTORY.toFile("history.json");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed to save history to file history.json", e);
|
logger.error("Failed to save history to file history.json", e);
|
||||||
}
|
}
|
||||||
@ -977,18 +1044,15 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RIP_COMPLETE:
|
case RIP_COMPLETE:
|
||||||
boolean alreadyInHistory = false;
|
|
||||||
RipStatusComplete rsc = (RipStatusComplete) msg.getObject();
|
RipStatusComplete rsc = (RipStatusComplete) msg.getObject();
|
||||||
String url = ripper.getURL().toExternalForm();
|
String url = ripper.getURL().toExternalForm();
|
||||||
for (int i = 0; i < historyListModel.size(); i++) {
|
if (HISTORY.containsURL(url)) {
|
||||||
HistoryEntry entry = (HistoryEntry) historyListModel.get(i);
|
// TODO update "modifiedDate" of entry in HISTORY
|
||||||
if (entry.url.equals(url)) {
|
HistoryEntry entry = HISTORY.getEntryByURL(url);
|
||||||
alreadyInHistory = true;
|
entry.count = rsc.count;
|
||||||
entry.modifiedDate = new Date();
|
entry.modifiedDate = new Date();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!alreadyInHistory) {
|
else {
|
||||||
HistoryEntry entry = new HistoryEntry();
|
HistoryEntry entry = new HistoryEntry();
|
||||||
entry.url = url;
|
entry.url = url;
|
||||||
entry.dir = rsc.getDir();
|
entry.dir = rsc.getDir();
|
||||||
@ -996,7 +1060,8 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
try {
|
try {
|
||||||
entry.title = ripper.getAlbumTitle(ripper.getURL());
|
entry.title = ripper.getAlbumTitle(ripper.getURL());
|
||||||
} catch (MalformedURLException e) { }
|
} catch (MalformedURLException e) { }
|
||||||
historyListModel.addElement(entry);
|
HISTORY.add(entry);
|
||||||
|
historyTableModel.fireTableDataChanged();
|
||||||
}
|
}
|
||||||
if (configPlaySound.isSelected()) {
|
if (configPlaySound.isSelected()) {
|
||||||
Utils.playSound("camera.wav");
|
Utils.playSound("camera.wav");
|
||||||
|
Loading…
Reference in New Issue
Block a user