Document *all* the things

This commit is contained in:
4pr0n 2014-03-09 00:20:22 -08:00
parent 8b7e471b1c
commit 43948fd320
7 changed files with 119 additions and 7 deletions

View File

@ -32,7 +32,6 @@ public abstract class AbstractRipper
protected DownloadThreadPool threadPool; protected DownloadThreadPool threadPool;
protected Observer observer = null; protected Observer observer = null;
protected int itemsTotal;
protected Map<URL, File> itemsPending = new HashMap<URL, File>(); protected Map<URL, File> itemsPending = new HashMap<URL, File>();
protected Map<URL, File> itemsCompleted = new HashMap<URL, File>(); protected Map<URL, File> itemsCompleted = new HashMap<URL, File>();
protected Map<URL, String> itemsErrored = new HashMap<URL, String>(); protected Map<URL, String> itemsErrored = new HashMap<URL, String>();
@ -107,6 +106,15 @@ public abstract class AbstractRipper
threadPool.addThread(new DownloadFileThread(url, saveAs, this)); threadPool.addThread(new DownloadFileThread(url, saveAs, this));
} }
/**
* Queues file to be downloaded and saved. With options.
* @param url
* URL to download.
* @param prefix
* Prefix to prepend to the saved filename.
* @param subdirectory
* Sub-directory of the working directory to save the images to.
*/
public void addURLToDownload(URL url, String prefix, String subdirectory) { public void addURLToDownload(URL url, String prefix, String subdirectory) {
String saveAs = url.toExternalForm(); String saveAs = url.toExternalForm();
saveAs = saveAs.substring(saveAs.lastIndexOf('/')+1); saveAs = saveAs.substring(saveAs.lastIndexOf('/')+1);
@ -137,17 +145,32 @@ public abstract class AbstractRipper
addURLToDownload(url, saveFileAs); addURLToDownload(url, saveFileAs);
} }
/**
* Waits for downloading threads to complete.
*/
protected void waitForThreads() { protected void waitForThreads() {
completed = false; completed = false;
threadPool.waitForThreads(); threadPool.waitForThreads();
} }
/**
* Notifies observers that source is being retrieved.
* @param url
* URL being retrieved
*/
public void retrievingSource(URL url) { public void retrievingSource(URL url) {
RipStatusMessage msg = new RipStatusMessage(STATUS.LOADING_RESOURCE, url); RipStatusMessage msg = new RipStatusMessage(STATUS.LOADING_RESOURCE, url);
observer.update(this, msg); observer.update(this, msg);
observer.notifyAll(); observer.notifyAll();
} }
/**
* Notifies observers that a file download has completed.
* @param url
* URL that was completed.
* @param saveAs
* Where the downloaded file is stored.
*/
public void downloadCompleted(URL url, File saveAs) { public void downloadCompleted(URL url, File saveAs) {
if (observer == null) { if (observer == null) {
return; return;
@ -167,6 +190,11 @@ public abstract class AbstractRipper
} }
} }
/**
* Notifies observers that a file could not be downloaded (includes a reason).
* @param url
* @param reason
*/
public void downloadErrored(URL url, String reason) { public void downloadErrored(URL url, String reason) {
if (observer == null) { if (observer == null) {
return; return;
@ -180,6 +208,12 @@ public abstract class AbstractRipper
} }
} }
/**
* Notify observers that a download could not be completed,
* but was not technically an "error".
* @param url
* @param message
*/
public void downloadProblem(URL url, String message) { public void downloadProblem(URL url, String message) {
if (observer == null) { if (observer == null) {
return; return;
@ -193,6 +227,9 @@ public abstract class AbstractRipper
} }
} }
/**
* Notifies observers and updates state if all files have been ripped.
*/
private void checkIfComplete() { private void checkIfComplete() {
if (!completed && itemsPending.size() == 0) { if (!completed && itemsPending.size() == 0) {
completed = true; completed = true;
@ -209,10 +246,20 @@ public abstract class AbstractRipper
return url; return url;
} }
/**
* @return
* Path to the directory in which all files
* ripped via this ripper will be stored.
*/
public File getWorkingDir() { public File getWorkingDir() {
return workingDir; return workingDir;
} }
/**
* Sets directory to save all ripped files to.
* @param url
* URL to define how the workin directory should be saved.
*/
public void setWorkingDir(URL url) throws IOException { public void setWorkingDir(URL url) throws IOException {
String path = Utils.getWorkingDirectory().getCanonicalPath(); String path = Utils.getWorkingDirectory().getCanonicalPath();
if (!path.endsWith(File.separator)) { if (!path.endsWith(File.separator)) {
@ -243,12 +290,16 @@ public abstract class AbstractRipper
return ripper; return ripper;
} catch (Exception e) { } catch (Exception e) {
// Incompatible rippers *will* throw exceptions during instantiation. // Incompatible rippers *will* throw exceptions during instantiation.
//logger.error("Exception while instantiating: " + constructor.getName(), e);
} }
} }
throw new Exception("No compatible ripper found"); throw new Exception("No compatible ripper found");
} }
/**
* @return
* List of constructors for all eligible Rippers.
* @throws Exception
*/
private static List<Constructor<?>> getRipperConstructors() throws Exception { private static List<Constructor<?>> getRipperConstructors() throws Exception {
List<Constructor<?>> constructors = new ArrayList<Constructor<?>>(); List<Constructor<?>> constructors = new ArrayList<Constructor<?>>();
for (Class<?> clazz : Utils.getClassesForPackage("com.rarchives.ripme.ripper.rippers")) { for (Class<?> clazz : Utils.getClassesForPackage("com.rarchives.ripme.ripper.rippers")) {
@ -259,6 +310,11 @@ public abstract class AbstractRipper
return constructors; return constructors;
} }
/**
* Sends an update message to the relevant observer(s) on this ripper.
* @param status
* @param message
*/
public void sendUpdate(STATUS status, Object message) { public void sendUpdate(STATUS status, Object message) {
if (observer == null) { if (observer == null) {
return; return;
@ -269,11 +325,19 @@ public abstract class AbstractRipper
} }
} }
/**
* @return
* Integer between 0 and 100 defining the progress of the album rip.
*/
public int getCompletionPercentage() { public int getCompletionPercentage() {
double total = itemsPending.size() + itemsErrored.size() + itemsCompleted.size(); double total = itemsPending.size() + itemsErrored.size() + itemsCompleted.size();
return (int) (100 * ( (total - itemsPending.size()) / total)); return (int) (100 * ( (total - itemsPending.size()) / total));
} }
/**
* @return
* Human-readable information on the status of the current rip.
*/
public String getStatusText() { public String getStatusText() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(getCompletionPercentage()) sb.append(getCompletionPercentage())
@ -284,6 +348,9 @@ public abstract class AbstractRipper
return sb.toString(); return sb.toString();
} }
/**
* Rips the album when the thread is invoked.
*/
public void run() { public void run() {
try { try {
rip(); rip();

View File

@ -12,6 +12,10 @@ import org.jsoup.Jsoup;
import com.rarchives.ripme.ui.RipStatusMessage.STATUS; import com.rarchives.ripme.ui.RipStatusMessage.STATUS;
import com.rarchives.ripme.utils.Utils; import com.rarchives.ripme.utils.Utils;
/**
* Thread for downloading files.
* Includes retry logic, observer notifications, and other goodies.
*/
public class DownloadFileThread extends Thread { public class DownloadFileThread extends Thread {
private static final Logger logger = Logger.getLogger(DownloadFileThread.class); private static final Logger logger = Logger.getLogger(DownloadFileThread.class);
@ -31,8 +35,11 @@ public class DownloadFileThread extends Thread {
this.retries = Utils.getConfigInteger("download.retries", 1); this.retries = Utils.getConfigInteger("download.retries", 1);
} }
/**
* Attempts to download the file. Retries as needed.
* Notifies observers upon completion/error/warn.
*/
public void run() { public void run() {
// Check if file already exists
if (saveAs.exists()) { if (saveAs.exists()) {
if (Utils.getConfigBoolean("file.overwrite", false)) { if (Utils.getConfigBoolean("file.overwrite", false)) {
logger.info("[!] Deleting existing file" + prettySaveAs); logger.info("[!] Deleting existing file" + prettySaveAs);

View File

@ -8,6 +8,9 @@ import org.apache.log4j.Logger;
import com.rarchives.ripme.utils.Utils; import com.rarchives.ripme.utils.Utils;
/**
* Simple wrapper around a FixedThreadPool.
*/
public class DownloadThreadPool { public class DownloadThreadPool {
private static final Logger logger = Logger.getLogger(DownloadThreadPool.class); private static final Logger logger = Logger.getLogger(DownloadThreadPool.class);
@ -34,9 +37,10 @@ public class DownloadThreadPool {
public void waitForThreads() { public void waitForThreads() {
threadPool.shutdown(); threadPool.shutdown();
try { try {
threadPool.awaitTermination(60, TimeUnit.SECONDS); // XXX What if some rips take longer than 120 seconds to complete?
threadPool.awaitTermination(120, TimeUnit.SECONDS);
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.error("Interrupted while waiting for threads to finish: ", e); logger.error("[!] Interrupted while waiting for threads to finish: ", e);
} }
} }
} }

View File

@ -4,6 +4,10 @@ import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
/**
* I have no idea why I made this interface. Everything is captured within the AbstractRipper.
* Oh well, here's to encapsulation and abstraction! (raises glass)
*/
public interface RipperInterface { public interface RipperInterface {
public void rip() throws IOException; public void rip() throws IOException;
public boolean canRip(URL url); public boolean canRip(URL url);

View File

@ -43,6 +43,9 @@ import org.apache.log4j.Logger;
import com.rarchives.ripme.ripper.AbstractRipper; import com.rarchives.ripme.ripper.AbstractRipper;
import com.rarchives.ripme.utils.Utils; import com.rarchives.ripme.utils.Utils;
/**
* Everything UI-related starts and ends here.
*/
public class MainWindow implements Runnable { public class MainWindow implements Runnable {
private static final Logger logger = Logger.getLogger(MainWindow.class); private static final Logger logger = Logger.getLogger(MainWindow.class);

View File

@ -1,5 +1,8 @@
package com.rarchives.ripme.ui; package com.rarchives.ripme.ui;
/**
* Contains information about downloaded files and rips, and their states.
*/
public class RipStatusMessage { public class RipStatusMessage {
public enum STATUS { public enum STATUS {

View File

@ -14,9 +14,12 @@ import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/**
* Common utility functions used in various places throughout the project.
*/
public class Utils { public class Utils {
public static final String RIP_DIRECTORY = "rips"; public static final String RIP_DIRECTORY = "rips";
private static final File configFile = new File("src/main/resources/rip.properties"); private static final File configFile = new File("src/main/resources/rip.properties");
private static final Logger logger = Logger.getLogger(Utils.class); private static final Logger logger = Logger.getLogger(Utils.class);
@ -29,6 +32,12 @@ public class Utils {
} }
} }
/**
* Get the root rips directory.
* @return
* Root directory to save rips to.
* @throws IOException
*/
public static File getWorkingDirectory() throws IOException { public static File getWorkingDirectory() throws IOException {
String path = new File(".").getCanonicalPath() + File.separator; String path = new File(".").getCanonicalPath() + File.separator;
path += RIP_DIRECTORY + File.separator; path += RIP_DIRECTORY + File.separator;
@ -75,10 +84,25 @@ public class Utils {
return prettySaveAs; return prettySaveAs;
} }
/**
* Removes the current working directory from a given filename
* @param file
* @return
* 'file' without the leading current working directory
*/
public static String removeCWD(String file) { public static String removeCWD(String file) {
return removeCWD(new File(file)); return removeCWD(new File(file));
} }
/**
* Get a list of all Classes within a package.
* Works with file system projects and jar files!
* Borrowed from StackOverflow, but I don't have a link :[
* @param pkgname
* The name of the package
* @return
* List of classes within the package
*/
public static ArrayList<Class<?>> getClassesForPackage(String pkgname) { public static ArrayList<Class<?>> getClassesForPackage(String pkgname) {
ArrayList<Class<?>> classes = new ArrayList<Class<?>>(); ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
String relPath = pkgname.replace('.', '/'); String relPath = pkgname.replace('.', '/');