Added basic GUI with event handling.
Long way to go, but it's a start.
This commit is contained in:
parent
04ce12b546
commit
07c73701db
@ -3,6 +3,8 @@ package com.rarchives.ripme;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.apache.commons.cli.BasicParser;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
@ -11,6 +13,7 @@ import org.apache.commons.cli.ParseException;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
||||
import com.rarchives.ripme.ui.MainWindow;
|
||||
import com.rarchives.ripme.utils.Utils;
|
||||
|
||||
/**
|
||||
@ -23,14 +26,19 @@ public class App {
|
||||
public static void main(String[] args) throws MalformedURLException {
|
||||
logger.debug("Initialized");
|
||||
|
||||
CommandLine cl = handleArguments(args);
|
||||
if (args.length > 0) {
|
||||
CommandLine cl = handleArguments(args);
|
||||
|
||||
try {
|
||||
URL url = new URL(cl.getOptionValue('u'));
|
||||
rip(url);
|
||||
} catch (MalformedURLException e) {
|
||||
logger.error("[!] Given URL is not valid. Expected URL format is http://domain.com/...");
|
||||
System.exit(-1);
|
||||
try {
|
||||
URL url = new URL(cl.getOptionValue('u'));
|
||||
rip(url);
|
||||
} catch (MalformedURLException e) {
|
||||
logger.error("[!] Given URL is not valid. Expected URL format is http://domain.com/...");
|
||||
System.exit(-1);
|
||||
}
|
||||
} else {
|
||||
MainWindow mw = new MainWindow();
|
||||
SwingUtilities.invokeLater(mw);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,21 +4,35 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.rarchives.ripme.ripper.rippers.ImagearnRipper;
|
||||
import com.rarchives.ripme.ripper.rippers.ImagefapRipper;
|
||||
import com.rarchives.ripme.ripper.rippers.ImgurRipper;
|
||||
import com.rarchives.ripme.ui.RipStatusMessage;
|
||||
import com.rarchives.ripme.ui.RipStatusMessage.STATUS;
|
||||
import com.rarchives.ripme.utils.Utils;
|
||||
|
||||
public abstract class AbstractRipper implements RipperInterface {
|
||||
public abstract class AbstractRipper
|
||||
extends Observable
|
||||
implements RipperInterface {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AbstractRipper.class);
|
||||
|
||||
protected URL url;
|
||||
protected File workingDir;
|
||||
protected DownloadThreadPool threadPool;
|
||||
protected Observer observer = null;
|
||||
|
||||
protected int itemsTotal;
|
||||
protected Map<URL, File> itemsPending = new HashMap<URL, File>();
|
||||
protected Map<URL, File> itemsCompleted = new HashMap<URL, File>();
|
||||
protected Map<URL, String> itemsErrored = new HashMap<URL, String>();
|
||||
|
||||
public abstract void rip() throws IOException;
|
||||
public abstract String getHost();
|
||||
@ -41,6 +55,10 @@ public abstract class AbstractRipper implements RipperInterface {
|
||||
setWorkingDir(url);
|
||||
this.threadPool = new DownloadThreadPool();
|
||||
}
|
||||
|
||||
public void setObserver(Observer obs) {
|
||||
this.observer = obs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues image to be downloaded and saved.
|
||||
@ -74,7 +92,13 @@ public abstract class AbstractRipper implements RipperInterface {
|
||||
* Path of the local file to save the content to.
|
||||
*/
|
||||
public void addURLToDownload(URL url, File saveAs) {
|
||||
threadPool.addThread(new DownloadFileThread(url, saveAs));
|
||||
if (itemsPending.containsKey(url) || itemsCompleted.containsKey(url)) {
|
||||
// Item is already downloaded/downloading, skip it.
|
||||
logger.info("Skipping duplicate URL: " + url);
|
||||
return;
|
||||
}
|
||||
itemsPending.put(url, saveAs);
|
||||
threadPool.addThread(new DownloadFileThread(url, saveAs, this));
|
||||
}
|
||||
|
||||
public void addURLToDownload(URL url, String prefix, String subdirectory) {
|
||||
@ -106,6 +130,46 @@ public abstract class AbstractRipper implements RipperInterface {
|
||||
addURLToDownload(url, saveFileAs);
|
||||
}
|
||||
|
||||
public void retrievingSource(URL url) {
|
||||
RipStatusMessage msg = new RipStatusMessage(STATUS.LOADING_RESOURCE, url);
|
||||
observer.update(this, msg);
|
||||
observer.notifyAll();
|
||||
}
|
||||
|
||||
public void downloadCompleted(URL url, File saveAs) {
|
||||
try {
|
||||
String path = saveAs.getCanonicalPath();
|
||||
RipStatusMessage msg = new RipStatusMessage(STATUS.DOWNLOAD_COMPLETE, path);
|
||||
synchronized(observer) {
|
||||
itemsPending.remove(url);
|
||||
itemsCompleted.put(url, saveAs);
|
||||
observer.update(this, msg);
|
||||
observer.notifyAll();
|
||||
checkIfComplete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception while updating observer: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void downloadErrored(URL url, String reason) {
|
||||
synchronized(observer) {
|
||||
itemsPending.remove(url);
|
||||
itemsErrored.put(url, reason);
|
||||
observer.update(this, new RipStatusMessage(STATUS.DOWNLOAD_ERRORED, url + " : " + reason));
|
||||
observer.notifyAll();
|
||||
checkIfComplete();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfComplete() {
|
||||
if (itemsPending.size() == 0) {
|
||||
logger.info("Rip completed!");
|
||||
observer.update(this, new RipStatusMessage(STATUS.RIP_COMPLETE, workingDir));
|
||||
observer.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public URL getURL() {
|
||||
return url;
|
||||
}
|
||||
@ -123,7 +187,7 @@ public abstract class AbstractRipper implements RipperInterface {
|
||||
}
|
||||
logger.debug("Set working directory to: " + this.workingDir);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds, instantiates, and returns a compatible ripper for given URL.
|
||||
* @param url
|
||||
@ -149,4 +213,12 @@ public abstract class AbstractRipper implements RipperInterface {
|
||||
} catch (IOException e) { }
|
||||
throw new Exception("No compatible ripper found");
|
||||
}
|
||||
|
||||
public void sendUpdate(STATUS status, Object message) {
|
||||
synchronized (observer) {
|
||||
observer.update(this, new RipStatusMessage(status, message));
|
||||
observer.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -9,6 +9,7 @@ import org.apache.log4j.Logger;
|
||||
import org.jsoup.Connection.Response;
|
||||
import org.jsoup.Jsoup;
|
||||
|
||||
import com.rarchives.ripme.ui.RipStatusMessage.STATUS;
|
||||
import com.rarchives.ripme.utils.Utils;
|
||||
|
||||
public class DownloadFileThread extends Thread {
|
||||
@ -18,13 +19,15 @@ public class DownloadFileThread extends Thread {
|
||||
private URL url;
|
||||
private File saveAs;
|
||||
private String prettySaveAs;
|
||||
private AbstractRipper observer;
|
||||
private int retries;
|
||||
|
||||
public DownloadFileThread(URL url, File saveAs) {
|
||||
public DownloadFileThread(URL url, File saveAs, AbstractRipper observer) {
|
||||
super();
|
||||
this.url = url;
|
||||
this.saveAs = saveAs;
|
||||
this.prettySaveAs = Utils.removeCWD(saveAs);
|
||||
this.observer = observer;
|
||||
this.retries = Utils.getConfigInteger("download.retries", 1);
|
||||
}
|
||||
|
||||
@ -32,10 +35,11 @@ public class DownloadFileThread extends Thread {
|
||||
// Check if file already exists
|
||||
if (saveAs.exists()) {
|
||||
if (Utils.getConfigBoolean("file.overwrite", false)) {
|
||||
logger.info("[!] File already exists and 'file.overwrite' is true, deleting: " + prettySaveAs);
|
||||
logger.info("[!] Deleting existing file" + prettySaveAs);
|
||||
saveAs.delete();
|
||||
} else {
|
||||
logger.info("[!] Skipping " + url + " -- file already exists: " + prettySaveAs);
|
||||
observer.downloadErrored(url, "File already exists: " + prettySaveAs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -44,6 +48,7 @@ public class DownloadFileThread extends Thread {
|
||||
do {
|
||||
try {
|
||||
logger.info(" Downloading file: " + url + (tries > 0 ? " Retry #" + tries : ""));
|
||||
observer.sendUpdate(STATUS.DOWNLOAD_STARTED, url.toExternalForm());
|
||||
tries += 1;
|
||||
Response response;
|
||||
response = Jsoup.connect(url.toExternalForm())
|
||||
@ -52,15 +57,18 @@ public class DownloadFileThread extends Thread {
|
||||
FileOutputStream out = (new FileOutputStream(saveAs));
|
||||
out.write(response.bodyAsBytes());
|
||||
out.close();
|
||||
observer.downloadCompleted(url, saveAs.getCanonicalFile());
|
||||
break; // Download successful: break out of infinite loop
|
||||
} catch (IOException e) {
|
||||
logger.error("[!] Exception while downloading file: " + url + " - " + e.getMessage());
|
||||
}
|
||||
if (tries > this.retries) {
|
||||
logger.error("[!] Exceeded maximum retries (" + this.retries + ") for URL " + url);
|
||||
observer.downloadErrored(url, "Failed to download " + url.toExternalForm());
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
observer.downloadCompleted(url, saveAs);
|
||||
logger.info("[+] Saved " + url + " as " + this.prettySaveAs);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
|
||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
||||
import com.rarchives.ripme.ui.RipStatusMessage.STATUS;
|
||||
|
||||
public class ImgurRipper extends AbstractRipper {
|
||||
|
||||
@ -67,7 +68,6 @@ public class ImgurRipper extends AbstractRipper {
|
||||
public void rip() throws IOException {
|
||||
switch (albumType) {
|
||||
case ALBUM:
|
||||
this.url = new URL(this.url.toExternalForm());
|
||||
// Fall-through
|
||||
case USER_ALBUM:
|
||||
ripAlbum(this.url);
|
||||
@ -92,6 +92,7 @@ public class ImgurRipper extends AbstractRipper {
|
||||
private void ripAlbum(URL url, String subdirectory) throws IOException {
|
||||
int index = 0;
|
||||
logger.info(" Retrieving " + url.toExternalForm());
|
||||
this.sendUpdate(STATUS.LOADING_RESOURCE, url.toExternalForm());
|
||||
Document doc = Jsoup.connect(url.toExternalForm()).get();
|
||||
|
||||
// Try to use embedded JSON to retrieve images
|
||||
|
91
src/main/java/com/rarchives/ripme/ui/MainWindow.java
Normal file
91
src/main/java/com/rarchives/ripme/ui/MainWindow.java
Normal file
@ -0,0 +1,91 @@
|
||||
package com.rarchives.ripme.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
||||
|
||||
|
||||
public class MainWindow implements Runnable {
|
||||
|
||||
private static final String WINDOW_TITLE = "RipMe";
|
||||
|
||||
private static JFrame mainFrame;
|
||||
private static JTextField ripTextfield;
|
||||
private static JButton ripButton;
|
||||
|
||||
private static JLabel ripStatus;
|
||||
|
||||
public MainWindow() {
|
||||
createUI();
|
||||
setupHandlers();
|
||||
}
|
||||
|
||||
private void createUI() {
|
||||
mainFrame = new JFrame(WINDOW_TITLE);
|
||||
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
ripTextfield = new JTextField("", 20);
|
||||
ripButton = new JButton("rip");
|
||||
ripStatus = new JLabel("inactive");
|
||||
mainFrame.getContentPane().add(ripTextfield, BorderLayout.WEST);
|
||||
mainFrame.getContentPane().add(ripButton, BorderLayout.EAST);
|
||||
mainFrame.getContentPane().add(ripStatus, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private void setupHandlers() {
|
||||
ripButton.addActionListener(new RipButtonHandler());
|
||||
}
|
||||
|
||||
public void run() {
|
||||
mainFrame.pack();
|
||||
mainFrame.setLocationRelativeTo(null);
|
||||
mainFrame.setVisible(true);
|
||||
}
|
||||
|
||||
public static void status(String text) {
|
||||
ripStatus.setText(text);
|
||||
}
|
||||
|
||||
class RipButtonHandler implements ActionListener {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
try {
|
||||
URL url = new URL(ripTextfield.getText());
|
||||
AbstractRipper ripper = AbstractRipper.getRipper(url);
|
||||
ripper.setObserver(new RipStatusHandler());
|
||||
ripper.rip();
|
||||
} catch (Exception e) {
|
||||
status("Error: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RipStatusHandler implements Observer {
|
||||
public void update(Observable observable, Object object) {
|
||||
RipStatusMessage msg = (RipStatusMessage) object;
|
||||
System.err.println("Observer update, object: " + object.toString());
|
||||
switch(msg.getStatus()) {
|
||||
case LOADING_RESOURCE:
|
||||
case DOWNLOAD_STARTED:
|
||||
case DOWNLOAD_COMPLETE:
|
||||
case DOWNLOAD_ERRORED:
|
||||
status((String) msg.getObject());
|
||||
break;
|
||||
case RIP_COMPLETE:
|
||||
File f = (File) msg.getObject();
|
||||
status("RIP COMPLETE: " + f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
src/main/java/com/rarchives/ripme/ui/RipStatusMessage.java
Normal file
37
src/main/java/com/rarchives/ripme/ui/RipStatusMessage.java
Normal file
@ -0,0 +1,37 @@
|
||||
package com.rarchives.ripme.ui;
|
||||
|
||||
public class RipStatusMessage {
|
||||
|
||||
public enum STATUS {
|
||||
LOADING_RESOURCE("Loading Resource"),
|
||||
DOWNLOAD_STARTED("Download Started"),
|
||||
DOWNLOAD_COMPLETE("Download Domplete"),
|
||||
DOWNLOAD_ERRORED("Download Errored"),
|
||||
RIP_COMPLETE("Rip Complete");
|
||||
|
||||
String value;
|
||||
STATUS(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private STATUS status;
|
||||
private Object object;
|
||||
|
||||
public RipStatusMessage(STATUS status, Object object) {
|
||||
this.status = status;
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public STATUS getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return status.value + ": " + object.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user