Added video ripper support, xvideos for now #18
This commit is contained in:
parent
fce0ecd8bf
commit
9946777e81
2
pom.xml
2
pom.xml
@ -4,7 +4,7 @@
|
|||||||
<groupId>com.rarchives.ripme</groupId>
|
<groupId>com.rarchives.ripme</groupId>
|
||||||
<artifactId>ripme</artifactId>
|
<artifactId>ripme</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.0.19</version>
|
<version>1.0.20</version>
|
||||||
<name>ripme</name>
|
<name>ripme</name>
|
||||||
<url>http://rip.rarchives.com</url>
|
<url>http://rip.rarchives.com</url>
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -6,10 +6,7 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Observable;
|
import java.util.Observable;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
@ -23,7 +20,7 @@ public abstract class AbstractRipper
|
|||||||
extends Observable
|
extends Observable
|
||||||
implements RipperInterface, Runnable {
|
implements RipperInterface, Runnable {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AbstractRipper.class);
|
protected static final Logger logger = Logger.getLogger(AbstractRipper.class);
|
||||||
|
|
||||||
protected static final String USER_AGENT =
|
protected static final String USER_AGENT =
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:27.0) Gecko/20100101 Firefox/27.0";
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:27.0) Gecko/20100101 Firefox/27.0";
|
||||||
@ -33,9 +30,6 @@ public abstract class AbstractRipper
|
|||||||
protected DownloadThreadPool threadPool;
|
protected DownloadThreadPool threadPool;
|
||||||
protected RipStatusHandler observer = null;
|
protected RipStatusHandler observer = null;
|
||||||
|
|
||||||
protected Map<URL, File> itemsPending = Collections.synchronizedMap(new HashMap<URL, File>());
|
|
||||||
protected Map<URL, File> itemsCompleted = Collections.synchronizedMap(new HashMap<URL, File>());
|
|
||||||
protected Map<URL, String> itemsErrored = Collections.synchronizedMap(new HashMap<URL, String>());
|
|
||||||
protected boolean completed = true;
|
protected boolean completed = true;
|
||||||
|
|
||||||
public abstract void rip() throws IOException;
|
public abstract void rip() throws IOException;
|
||||||
@ -110,17 +104,7 @@ public abstract class AbstractRipper
|
|||||||
* @param saveAs
|
* @param saveAs
|
||||||
* Path of the local file to save the content to.
|
* Path of the local file to save the content to.
|
||||||
*/
|
*/
|
||||||
public void addURLToDownload(URL url, File saveAs) {
|
public abstract void addURLToDownload(URL url, File saveAs);
|
||||||
if (itemsPending.containsKey(url)
|
|
||||||
|| itemsCompleted.containsKey(url)
|
|
||||||
|| itemsErrored.containsKey(url)) {
|
|
||||||
// Item is already downloaded/downloading, skip it.
|
|
||||||
logger.info("[!] Skipping " + url + " -- already attempted: " + Utils.removeCWD(saveAs));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
itemsPending.put(url, saveAs);
|
|
||||||
threadPool.addThread(new DownloadFileThread(url, saveAs, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues file to be downloaded and saved. With options.
|
* Queues file to be downloaded and saved. With options.
|
||||||
@ -192,67 +176,30 @@ public abstract class AbstractRipper
|
|||||||
* @param saveAs
|
* @param saveAs
|
||||||
* Where the downloaded file is stored.
|
* Where the downloaded file is stored.
|
||||||
*/
|
*/
|
||||||
public void downloadCompleted(URL url, File saveAs) {
|
public abstract void downloadCompleted(URL url, File saveAs);
|
||||||
if (observer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String path = Utils.removeCWD(saveAs);
|
|
||||||
RipStatusMessage msg = new RipStatusMessage(STATUS.DOWNLOAD_COMPLETE, path);
|
|
||||||
itemsPending.remove(url);
|
|
||||||
itemsCompleted.put(url, saveAs);
|
|
||||||
observer.update(this, msg);
|
|
||||||
|
|
||||||
checkIfComplete();
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Exception while updating observer: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies observers that a file could not be downloaded (includes a reason).
|
* Notifies observers that a file could not be downloaded (includes a reason).
|
||||||
* @param url
|
* @param url
|
||||||
* @param reason
|
* @param reason
|
||||||
*/
|
*/
|
||||||
public void downloadErrored(URL url, String reason) {
|
public abstract void downloadErrored(URL url, String reason);
|
||||||
if (observer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
itemsPending.remove(url);
|
|
||||||
itemsErrored.put(url, reason);
|
|
||||||
observer.update(this, new RipStatusMessage(STATUS.DOWNLOAD_ERRORED, url + " : " + reason));
|
|
||||||
|
|
||||||
checkIfComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify observers that a download could not be completed,
|
* Notify observers that a download could not be completed,
|
||||||
* but was not technically an "error".
|
* but was not technically an "error".
|
||||||
* @param url
|
* @param url
|
||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
public void downloadProblem(URL url, String message) {
|
public abstract void downloadProblem(URL url, String message);
|
||||||
if (observer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemsPending.remove(url);
|
|
||||||
itemsErrored.put(url, message);
|
|
||||||
observer.update(this, new RipStatusMessage(STATUS.DOWNLOAD_WARN, url + " : " + message));
|
|
||||||
|
|
||||||
|
|
||||||
checkIfComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies observers and updates state if all files have been ripped.
|
* Notifies observers and updates state if all files have been ripped.
|
||||||
*/
|
*/
|
||||||
private void checkIfComplete() {
|
protected void checkIfComplete() {
|
||||||
if (observer == null) {
|
if (observer == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!completed && itemsPending.isEmpty()) {
|
if (!completed) {
|
||||||
completed = true;
|
completed = true;
|
||||||
logger.info(" Rip completed!");
|
logger.info(" Rip completed!");
|
||||||
|
|
||||||
@ -274,26 +221,7 @@ public abstract class AbstractRipper
|
|||||||
return workingDir;
|
return workingDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public abstract void setWorkingDir(URL url) throws IOException;
|
||||||
* 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 {
|
|
||||||
String path = Utils.getWorkingDirectory().getCanonicalPath();
|
|
||||||
if (!path.endsWith(File.separator)) {
|
|
||||||
path += File.separator;
|
|
||||||
}
|
|
||||||
String title = getAlbumTitle(this.url);
|
|
||||||
title = Utils.filesystemSafe(title);
|
|
||||||
path += title + File.separator;
|
|
||||||
this.workingDir = new File(path);
|
|
||||||
if (!this.workingDir.exists()) {
|
|
||||||
logger.info("[+] Creating directory: " + Utils.removeCWD(this.workingDir));
|
|
||||||
this.workingDir.mkdirs();
|
|
||||||
}
|
|
||||||
logger.debug("Set working directory to: " + this.workingDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAlbumTitle(URL url) throws MalformedURLException {
|
public String getAlbumTitle(URL url) throws MalformedURLException {
|
||||||
return getHost() + "_" + getGID(url);
|
return getHost() + "_" + getGID(url);
|
||||||
@ -309,9 +237,17 @@ public abstract class AbstractRipper
|
|||||||
* If no compatible rippers can be found.
|
* If no compatible rippers can be found.
|
||||||
*/
|
*/
|
||||||
public static AbstractRipper getRipper(URL url) throws Exception {
|
public static AbstractRipper getRipper(URL url) throws Exception {
|
||||||
for (Constructor<?> constructor : getRipperConstructors()) {
|
for (Constructor<?> constructor : getRipperConstructors("com.rarchives.ripme.ripper.rippers")) {
|
||||||
try {
|
try {
|
||||||
AbstractRipper ripper = (AbstractRipper) constructor.newInstance(url);
|
AlbumRipper ripper = (AlbumRipper) constructor.newInstance(url);
|
||||||
|
return ripper;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Incompatible rippers *will* throw exceptions during instantiation.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Constructor<?> constructor : getRipperConstructors("com.rarchives.ripme.ripper.rippers.video")) {
|
||||||
|
try {
|
||||||
|
VideoRipper ripper = (VideoRipper) constructor.newInstance(url);
|
||||||
return ripper;
|
return ripper;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Incompatible rippers *will* throw exceptions during instantiation.
|
// Incompatible rippers *will* throw exceptions during instantiation.
|
||||||
@ -325,9 +261,9 @@ public abstract class AbstractRipper
|
|||||||
* List of constructors for all eligible Rippers.
|
* List of constructors for all eligible Rippers.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private static List<Constructor<?>> getRipperConstructors() throws Exception {
|
private static List<Constructor<?>> getRipperConstructors(String pkg) 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(pkg)) {
|
||||||
if (AbstractRipper.class.isAssignableFrom(clazz)) {
|
if (AbstractRipper.class.isAssignableFrom(clazz)) {
|
||||||
constructors.add( (Constructor<?>) clazz.getConstructor(URL.class) );
|
constructors.add( (Constructor<?>) clazz.getConstructor(URL.class) );
|
||||||
}
|
}
|
||||||
@ -347,28 +283,9 @@ public abstract class AbstractRipper
|
|||||||
observer.update(this, new RipStatusMessage(status, message));
|
observer.update(this, new RipStatusMessage(status, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public abstract int getCompletionPercentage();
|
||||||
* @return
|
|
||||||
* Integer between 0 and 100 defining the progress of the album rip.
|
|
||||||
*/
|
|
||||||
public int getCompletionPercentage() {
|
|
||||||
double total = itemsPending.size() + itemsErrored.size() + itemsCompleted.size();
|
|
||||||
return (int) (100 * ( (total - itemsPending.size()) / total));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public abstract String getStatusText();
|
||||||
* @return
|
|
||||||
* Human-readable information on the status of the current rip.
|
|
||||||
*/
|
|
||||||
public String getStatusText() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(getCompletionPercentage())
|
|
||||||
.append("% ")
|
|
||||||
.append("- Pending: " ).append(itemsPending.size())
|
|
||||||
.append(", Completed: ").append(itemsCompleted.size())
|
|
||||||
.append(", Errored: " ).append(itemsErrored.size());
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rips the album when the thread is invoked.
|
* Rips the album when the thread is invoked.
|
||||||
@ -382,4 +299,10 @@ public abstract class AbstractRipper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBytesTotal(int bytes) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
public void setBytesCompleted(int bytes) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
146
src/main/java/com/rarchives/ripme/ripper/AlbumRipper.java
Normal file
146
src/main/java/com/rarchives/ripme/ripper/AlbumRipper.java
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package com.rarchives.ripme.ripper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.rarchives.ripme.ui.RipStatusMessage;
|
||||||
|
import com.rarchives.ripme.ui.RipStatusMessage.STATUS;
|
||||||
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
|
public abstract class AlbumRipper extends AbstractRipper {
|
||||||
|
|
||||||
|
protected Map<URL, File> itemsPending = Collections.synchronizedMap(new HashMap<URL, File>());
|
||||||
|
protected Map<URL, File> itemsCompleted = Collections.synchronizedMap(new HashMap<URL, File>());
|
||||||
|
protected Map<URL, String> itemsErrored = Collections.synchronizedMap(new HashMap<URL, String>());
|
||||||
|
|
||||||
|
public AlbumRipper(URL url) throws IOException {
|
||||||
|
super(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean canRip(URL url);
|
||||||
|
public abstract URL sanitizeURL(URL url) throws MalformedURLException;
|
||||||
|
public abstract void rip() throws IOException;
|
||||||
|
public abstract String getHost();
|
||||||
|
public abstract String getGID(URL url) throws MalformedURLException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addURLToDownload(URL url, File saveAs) {
|
||||||
|
if (itemsPending.containsKey(url)
|
||||||
|
|| itemsCompleted.containsKey(url)
|
||||||
|
|| itemsErrored.containsKey(url)) {
|
||||||
|
// Item is already downloaded/downloading, skip it.
|
||||||
|
logger.info("[!] Skipping " + url + " -- already attempted: " + Utils.removeCWD(saveAs));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itemsPending.put(url, saveAs);
|
||||||
|
threadPool.addThread(new DownloadFileThread(url, saveAs, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadCompleted(URL url, File saveAs) {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String path = Utils.removeCWD(saveAs);
|
||||||
|
RipStatusMessage msg = new RipStatusMessage(STATUS.DOWNLOAD_COMPLETE, path);
|
||||||
|
itemsPending.remove(url);
|
||||||
|
itemsCompleted.put(url, saveAs);
|
||||||
|
observer.update(this, msg);
|
||||||
|
|
||||||
|
checkIfComplete();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Exception while updating observer: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadErrored(URL url, String reason) {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itemsPending.remove(url);
|
||||||
|
itemsErrored.put(url, reason);
|
||||||
|
observer.update(this, new RipStatusMessage(STATUS.DOWNLOAD_ERRORED, url + " : " + reason));
|
||||||
|
|
||||||
|
checkIfComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadProblem(URL url, String message) {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsPending.remove(url);
|
||||||
|
itemsErrored.put(url, message);
|
||||||
|
observer.update(this, new RipStatusMessage(STATUS.DOWNLOAD_WARN, url + " : " + message));
|
||||||
|
|
||||||
|
checkIfComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies observers and updates state if all files have been ripped.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void checkIfComplete() {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (itemsPending.isEmpty()) {
|
||||||
|
super.checkIfComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets directory to save all ripped files to.
|
||||||
|
* @param url
|
||||||
|
* URL to define how the workin directory should be saved.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setWorkingDir(URL url) throws IOException {
|
||||||
|
String path = Utils.getWorkingDirectory().getCanonicalPath();
|
||||||
|
if (!path.endsWith(File.separator)) {
|
||||||
|
path += File.separator;
|
||||||
|
}
|
||||||
|
String title = getAlbumTitle(this.url);
|
||||||
|
title = Utils.filesystemSafe(title);
|
||||||
|
path += title + File.separator;
|
||||||
|
this.workingDir = new File(path);
|
||||||
|
if (!this.workingDir.exists()) {
|
||||||
|
logger.info("[+] Creating directory: " + Utils.removeCWD(this.workingDir));
|
||||||
|
this.workingDir.mkdirs();
|
||||||
|
}
|
||||||
|
logger.debug("Set working directory to: " + this.workingDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* Integer between 0 and 100 defining the progress of the album rip.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getCompletionPercentage() {
|
||||||
|
double total = itemsPending.size() + itemsErrored.size() + itemsCompleted.size();
|
||||||
|
return (int) (100 * ( (total - itemsPending.size()) / total));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* Human-readable information on the status of the current rip.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getStatusText() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getCompletionPercentage())
|
||||||
|
.append("% ")
|
||||||
|
.append("- Pending: " ).append(itemsPending.size())
|
||||||
|
.append(", Completed: ").append(itemsCompleted.size())
|
||||||
|
.append(", Errored: " ).append(itemsErrored.size());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
package com.rarchives.ripme.ripper;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import com.rarchives.ripme.ui.RipStatusMessage.STATUS;
|
||||||
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread for downloading files.
|
||||||
|
* Includes retry logic, observer notifications, and other goodies.
|
||||||
|
*/
|
||||||
|
public class DownloadVideoThread extends Thread {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(DownloadVideoThread.class);
|
||||||
|
|
||||||
|
private URL url;
|
||||||
|
private File saveAs;
|
||||||
|
private String prettySaveAs;
|
||||||
|
private AbstractRipper observer;
|
||||||
|
private int retries;
|
||||||
|
|
||||||
|
public DownloadVideoThread(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to download the file. Retries as needed.
|
||||||
|
* Notifies observers upon completion/error/warn.
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
observer.stopCheck();
|
||||||
|
} catch (IOException e) {
|
||||||
|
observer.downloadErrored(url, "Download interrupted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (saveAs.exists()) {
|
||||||
|
if (Utils.getConfigBoolean("file.overwrite", false)) {
|
||||||
|
logger.info("[!] Deleting existing file" + prettySaveAs);
|
||||||
|
saveAs.delete();
|
||||||
|
} else {
|
||||||
|
logger.info("[!] Skipping " + url + " -- file already exists: " + prettySaveAs);
|
||||||
|
observer.downloadProblem(url, "File already exists: " + prettySaveAs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesTotal, bytesDownloaded = 0;
|
||||||
|
try {
|
||||||
|
bytesTotal = getTotalBytes(this.url);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Failed to get file size at " + this.url, e);
|
||||||
|
observer.downloadErrored(this.url, "Failed to get file size of " + this.url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
observer.setBytesTotal(bytesTotal);
|
||||||
|
observer.sendUpdate(STATUS.TOTAL_BYTES, bytesTotal);
|
||||||
|
logger.info("Size of file at " + this.url + " = " + bytesTotal + "b");
|
||||||
|
|
||||||
|
int tries = 0; // Number of attempts to download
|
||||||
|
do {
|
||||||
|
InputStream bis = null; OutputStream fos = null;
|
||||||
|
byte[] data = new byte[1024]; int bytesRead;
|
||||||
|
try {
|
||||||
|
logger.info(" Downloading file: " + url + (tries > 0 ? " Retry #" + tries : ""));
|
||||||
|
observer.sendUpdate(STATUS.DOWNLOAD_STARTED, url.toExternalForm());
|
||||||
|
tries += 1;
|
||||||
|
bis = new BufferedInputStream(this.url.openStream());
|
||||||
|
fos = new FileOutputStream(saveAs);
|
||||||
|
while ( (bytesRead = bis.read(data)) != -1) {
|
||||||
|
try {
|
||||||
|
observer.stopCheck();
|
||||||
|
} catch (IOException e) {
|
||||||
|
observer.downloadErrored(url, "Download interrupted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fos.write(data, 0, bytesRead);
|
||||||
|
bytesDownloaded += bytesRead;
|
||||||
|
observer.setBytesCompleted(bytesDownloaded);
|
||||||
|
observer.sendUpdate(STATUS.COMPLETED_BYTES, bytesDownloaded);
|
||||||
|
}
|
||||||
|
bis.close();
|
||||||
|
fos.close();
|
||||||
|
break; // Download successful: break out of infinite loop
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("[!] Exception while downloading file: " + url + " - " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
// Close any open streams
|
||||||
|
try {
|
||||||
|
if (bis != null) { bis.close(); }
|
||||||
|
} catch (IOException e) { }
|
||||||
|
try {
|
||||||
|
if (fos != null) { fos.close(); }
|
||||||
|
} catch (IOException e) { }
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getTotalBytes(URL url) throws IOException {
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod("HEAD");
|
||||||
|
return conn.getContentLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
121
src/main/java/com/rarchives/ripme/ripper/VideoRipper.java
Normal file
121
src/main/java/com/rarchives/ripme/ripper/VideoRipper.java
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package com.rarchives.ripme.ripper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import com.rarchives.ripme.ui.RipStatusMessage;
|
||||||
|
import com.rarchives.ripme.ui.RipStatusMessage.STATUS;
|
||||||
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
|
public abstract class VideoRipper extends AbstractRipper {
|
||||||
|
|
||||||
|
private int bytesTotal = 1,
|
||||||
|
bytesCompleted = 1;
|
||||||
|
|
||||||
|
public VideoRipper(URL url) throws IOException {
|
||||||
|
super(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean canRip(URL url);
|
||||||
|
public abstract void rip() throws IOException;
|
||||||
|
public abstract String getHost();
|
||||||
|
public abstract String getGID(URL url) throws MalformedURLException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBytesTotal(int bytes) {
|
||||||
|
this.bytesTotal = bytes;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setBytesCompleted(int bytes) {
|
||||||
|
this.bytesCompleted = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addURLToDownload(URL url, File saveAs) {
|
||||||
|
threadPool.addThread(new DownloadVideoThread(url, saveAs, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWorkingDir(URL url) throws IOException {
|
||||||
|
String path = Utils.getWorkingDirectory().getCanonicalPath();
|
||||||
|
if (!path.endsWith(File.separator)) {
|
||||||
|
path += File.separator;
|
||||||
|
}
|
||||||
|
path += "videos" + File.separator;
|
||||||
|
this.workingDir = new File(path);
|
||||||
|
if (!this.workingDir.exists()) {
|
||||||
|
logger.info("[+] Creating directory: " + Utils.removeCWD(this.workingDir));
|
||||||
|
this.workingDir.mkdirs();
|
||||||
|
}
|
||||||
|
logger.debug("Set working directory to: " + this.workingDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCompletionPercentage() {
|
||||||
|
return (int) (100 * (bytesCompleted / (float) bytesTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadCompleted(URL url, File saveAs) {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String path = Utils.removeCWD(saveAs);
|
||||||
|
RipStatusMessage msg = new RipStatusMessage(STATUS.DOWNLOAD_COMPLETE, path);
|
||||||
|
observer.update(this, msg);
|
||||||
|
|
||||||
|
checkIfComplete();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Exception while updating observer: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void downloadErrored(URL url, String reason) {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
observer.update(this, new RipStatusMessage(STATUS.DOWNLOAD_ERRORED, url + " : " + reason));
|
||||||
|
checkIfComplete();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void downloadProblem(URL url, String message) {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
observer.update(this, new RipStatusMessage(STATUS.DOWNLOAD_WARN, url + " : " + message));
|
||||||
|
checkIfComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStatusText() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getCompletionPercentage())
|
||||||
|
.append("% ")
|
||||||
|
.append(" - ")
|
||||||
|
.append(Utils.bytesToHumanReadable(bytesCompleted))
|
||||||
|
.append(" / ")
|
||||||
|
.append(Utils.bytesToHumanReadable(bytesTotal));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL sanitizeURL(URL url) throws MalformedURLException {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies observers and updates state if all files have been ripped.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void checkIfComplete() {
|
||||||
|
if (observer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bytesCompleted >= bytesTotal) {
|
||||||
|
super.checkIfComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,9 +13,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class ChanRipper extends AbstractRipper {
|
public class ChanRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ChanRipper.class);
|
private static final Logger logger = Logger.getLogger(ChanRipper.class);
|
||||||
|
|
||||||
|
@ -18,10 +18,10 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
import com.rarchives.ripme.utils.Utils;
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
public class DeviantartRipper extends AbstractRipper {
|
public class DeviantartRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "deviantart.com",
|
private static final String DOMAIN = "deviantart.com",
|
||||||
HOST = "deviantart";
|
HOST = "deviantart";
|
||||||
|
@ -12,9 +12,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class EightmusesRipper extends AbstractRipper {
|
public class EightmusesRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "8muses.com",
|
private static final String DOMAIN = "8muses.com",
|
||||||
HOST = "8muses";
|
HOST = "8muses";
|
||||||
|
@ -11,10 +11,10 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
import com.rarchives.ripme.utils.Utils;
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
public class GonewildRipper extends AbstractRipper {
|
public class GonewildRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String HOST = "gonewild";
|
private static final String HOST = "gonewild";
|
||||||
private static final Logger logger = Logger.getLogger(GonewildRipper.class);
|
private static final Logger logger = Logger.getLogger(GonewildRipper.class);
|
||||||
|
@ -11,9 +11,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class ImagearnRipper extends AbstractRipper {
|
public class ImagearnRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "imagearn.com",
|
private static final String DOMAIN = "imagearn.com",
|
||||||
HOST = "imagearn";
|
HOST = "imagearn";
|
||||||
|
@ -11,9 +11,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class ImagefapRipper extends AbstractRipper {
|
public class ImagefapRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "imagefap.com",
|
private static final String DOMAIN = "imagefap.com",
|
||||||
HOST = "imagefap";
|
HOST = "imagefap";
|
||||||
|
@ -18,11 +18,11 @@ import org.jsoup.nodes.Document;
|
|||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
import org.jsoup.select.Elements;
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
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;
|
||||||
|
|
||||||
public class ImgurRipper extends AbstractRipper {
|
public class ImgurRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "imgur.com",
|
private static final String DOMAIN = "imgur.com",
|
||||||
HOST = "imgur";
|
HOST = "imgur";
|
||||||
|
@ -13,9 +13,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class InstagramRipper extends AbstractRipper {
|
public class InstagramRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "instagram.com",
|
private static final String DOMAIN = "instagram.com",
|
||||||
HOST = "instagram";
|
HOST = "instagram";
|
||||||
|
@ -11,9 +11,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class KinkyshareRipper extends AbstractRipper {
|
public class KinkyshareRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String HOST = "kinkyshare";
|
private static final String HOST = "kinkyshare";
|
||||||
private static final Logger logger = Logger.getLogger(KinkyshareRipper.class);
|
private static final Logger logger = Logger.getLogger(KinkyshareRipper.class);
|
||||||
|
@ -11,10 +11,10 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
import com.rarchives.ripme.ripper.DownloadThreadPool;
|
import com.rarchives.ripme.ripper.DownloadThreadPool;
|
||||||
|
|
||||||
public class MotherlessRipper extends AbstractRipper {
|
public class MotherlessRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "motherless.com",
|
private static final String DOMAIN = "motherless.com",
|
||||||
HOST = "motherless";
|
HOST = "motherless";
|
||||||
|
@ -2,6 +2,7 @@ package com.rarchives.ripme.ripper.rippers;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@ -14,12 +15,11 @@ import org.json.JSONTokener;
|
|||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
import com.rarchives.ripme.utils.RipUtils;
|
import com.rarchives.ripme.utils.RipUtils;
|
||||||
import com.rarchives.ripme.utils.Utils;
|
import com.rarchives.ripme.utils.Utils;
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
|
|
||||||
public class RedditRipper extends AbstractRipper {
|
public class RedditRipper extends AlbumRipper {
|
||||||
|
|
||||||
public RedditRipper(URL url) throws IOException {
|
public RedditRipper(URL url) throws IOException {
|
||||||
super(url);
|
super(url);
|
||||||
|
@ -12,10 +12,10 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
import com.rarchives.ripme.ripper.DownloadThreadPool;
|
import com.rarchives.ripme.ripper.DownloadThreadPool;
|
||||||
|
|
||||||
public class SeeniveRipper extends AbstractRipper {
|
public class SeeniveRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "seenive.com",
|
private static final String DOMAIN = "seenive.com",
|
||||||
HOST = "seenive";
|
HOST = "seenive";
|
||||||
|
@ -12,10 +12,10 @@ import org.json.JSONObject;
|
|||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
import com.rarchives.ripme.utils.Utils;
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
public class TumblrRipper extends AbstractRipper {
|
public class TumblrRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "tumblr.com",
|
private static final String DOMAIN = "tumblr.com",
|
||||||
HOST = "tumblr";
|
HOST = "tumblr";
|
||||||
|
@ -16,10 +16,10 @@ import org.json.JSONTokener;
|
|||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
import com.rarchives.ripme.utils.Utils;
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
public class TwitterRipper extends AbstractRipper {
|
public class TwitterRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "twitter.com",
|
private static final String DOMAIN = "twitter.com",
|
||||||
HOST = "twitter";
|
HOST = "twitter";
|
||||||
|
@ -12,9 +12,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class VineboxRipper extends AbstractRipper {
|
public class VineboxRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "vinebox.co",
|
private static final String DOMAIN = "vinebox.co",
|
||||||
HOST = "vinebox";
|
HOST = "vinebox";
|
||||||
|
@ -18,9 +18,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class VkRipper extends AbstractRipper {
|
public class VkRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "vk.com",
|
private static final String DOMAIN = "vk.com",
|
||||||
HOST = "vk";
|
HOST = "vk";
|
||||||
|
@ -11,9 +11,9 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
import com.rarchives.ripme.ripper.AlbumRipper;
|
||||||
|
|
||||||
public class XhamsterRipper extends AbstractRipper {
|
public class XhamsterRipper extends AlbumRipper {
|
||||||
|
|
||||||
private static final String DOMAIN = "xhamster.com",
|
private static final String DOMAIN = "xhamster.com",
|
||||||
HOST = "xhamster";
|
HOST = "xhamster";
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.rarchives.ripme.ripper.rippers.video;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import com.rarchives.ripme.ripper.VideoRipper;
|
||||||
|
|
||||||
|
public class XvideosRipper extends VideoRipper {
|
||||||
|
|
||||||
|
private static final String HOST = "xvideos";
|
||||||
|
private static final Logger logger = Logger.getLogger(XvideosRipper.class);
|
||||||
|
|
||||||
|
public XvideosRipper(URL url) throws IOException {
|
||||||
|
super(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHost() {
|
||||||
|
return HOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRip(URL url) {
|
||||||
|
Pattern p = Pattern.compile("^https?://[wm.]*xvideos\\.com/video[0-9]+.*$");
|
||||||
|
Matcher m = p.matcher(url.toExternalForm());
|
||||||
|
return m.matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlbumTitle(URL url) {
|
||||||
|
return "videos";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL sanitizeURL(URL url) throws MalformedURLException {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getGID(URL url) throws MalformedURLException {
|
||||||
|
Pattern p = Pattern.compile("^https?://[wm.]*xvideos\\.com/video([0-9]+).*$");
|
||||||
|
Matcher m = p.matcher(url.toExternalForm());
|
||||||
|
if (m.matches()) {
|
||||||
|
return m.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MalformedURLException(
|
||||||
|
"Expected xvideo format:"
|
||||||
|
+ "xvideos.com/video####"
|
||||||
|
+ " Got: " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rip() throws IOException {
|
||||||
|
logger.info(" Retrieving " + this.url.toExternalForm());
|
||||||
|
Document doc = Jsoup.connect(this.url.toExternalForm())
|
||||||
|
.userAgent(USER_AGENT)
|
||||||
|
.get();
|
||||||
|
Elements embeds = doc.select("embed");
|
||||||
|
if (embeds.size() == 0) {
|
||||||
|
throw new IOException("Could not find Embed code at " + url);
|
||||||
|
}
|
||||||
|
Element embed = embeds.get(0);
|
||||||
|
String vars = embed.attr("flashvars");
|
||||||
|
for (String var : vars.split("&")) {
|
||||||
|
if (var.startsWith("flv_url=")) {
|
||||||
|
String vidUrl = var.substring("flv_url=".length());
|
||||||
|
vidUrl = URLDecoder.decode(vidUrl, "UTF-8");
|
||||||
|
addURLToDownload(new URL(vidUrl), HOST + "_" + getGID(this.url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
waitForThreads();
|
||||||
|
}
|
||||||
|
}
|
@ -721,6 +721,13 @@ public class MainWindow implements Runnable, RipStatusHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
mainFrame.pack();
|
mainFrame.pack();
|
||||||
|
break;
|
||||||
|
case COMPLETED_BYTES:
|
||||||
|
// Update completed bytes
|
||||||
|
break;
|
||||||
|
case TOTAL_BYTES:
|
||||||
|
// Update total bytes
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ public class RipStatusMessage {
|
|||||||
DOWNLOAD_COMPLETE("Download Complete"),
|
DOWNLOAD_COMPLETE("Download Complete"),
|
||||||
DOWNLOAD_ERRORED("Download Errored"),
|
DOWNLOAD_ERRORED("Download Errored"),
|
||||||
RIP_COMPLETE("Rip Complete"),
|
RIP_COMPLETE("Rip Complete"),
|
||||||
DOWNLOAD_WARN("Download problem");
|
DOWNLOAD_WARN("Download problem"),
|
||||||
|
TOTAL_BYTES("Total bytes"),
|
||||||
|
COMPLETED_BYTES("Completed bytes");
|
||||||
|
|
||||||
String value;
|
String value;
|
||||||
STATUS(String value) {
|
STATUS(String value) {
|
||||||
|
@ -19,7 +19,7 @@ import org.jsoup.nodes.Document;
|
|||||||
public class UpdateUtils {
|
public class UpdateUtils {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(UpdateUtils.class);
|
private static final Logger logger = Logger.getLogger(UpdateUtils.class);
|
||||||
private static final String DEFAULT_VERSION = "1.0.19";
|
private static final String DEFAULT_VERSION = "1.0.20";
|
||||||
private static final String updateJsonURL = "http://rarchives.com/ripme.json";
|
private static final String updateJsonURL = "http://rarchives.com/ripme.json";
|
||||||
private static final String updateJarURL = "http://rarchives.com/ripme.jar";
|
private static final String updateJarURL = "http://rarchives.com/ripme.jar";
|
||||||
private static final String mainFileName = "ripme.jar";
|
private static final String mainFileName = "ripme.jar";
|
||||||
|
@ -247,4 +247,15 @@ public class Utils {
|
|||||||
.replaceAll("__", "_")
|
.replaceAll("__", "_")
|
||||||
.replaceAll("_+$", "");
|
.replaceAll("_+$", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String bytesToHumanReadable(int bytes) {
|
||||||
|
float fbytes = (float) bytes;
|
||||||
|
String[] mags = new String[] {"", "k", "m", "g", "t"};
|
||||||
|
int magIndex = 0;
|
||||||
|
while (fbytes >= 1024) {
|
||||||
|
fbytes /= 1024;
|
||||||
|
magIndex++;
|
||||||
|
}
|
||||||
|
return String.format("%.2f%sb", fbytes, mags[magIndex]);
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,6 +4,8 @@ import junit.framework.Test;
|
|||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import junit.framework.TestSuite;
|
import junit.framework.TestSuite;
|
||||||
|
|
||||||
|
import com.rarchives.ripme.utils.Utils;
|
||||||
|
|
||||||
public class AppTest extends TestCase {
|
public class AppTest extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Create the test case
|
* Create the test case
|
||||||
@ -25,6 +27,7 @@ public class AppTest extends TestCase {
|
|||||||
* Rigourous Test :-)
|
* Rigourous Test :-)
|
||||||
*/
|
*/
|
||||||
public void testApp() {
|
public void testApp() {
|
||||||
|
System.err.println(Utils.bytesToHumanReadable(1023 * 5000));
|
||||||
assertTrue( true );
|
assertTrue( true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.rarchives.ripme.tst.ripper.rippers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.rarchives.ripme.ripper.rippers.video.XvideosRipper;
|
||||||
|
|
||||||
|
public class VideoRippersTest extends RippersTest {
|
||||||
|
|
||||||
|
public void testXvideosRipper() throws IOException {
|
||||||
|
if (false && !DOWNLOAD_CONTENT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<URL> contentURLs = new ArrayList<URL>();
|
||||||
|
contentURLs.add(new URL("http://www.xvideos.com/video1428195/stephanie_first_time_anal"));
|
||||||
|
contentURLs.add(new URL("http://www.xvideos.com/video7136868/vid-20140205-wa0011"));
|
||||||
|
for (URL url : contentURLs) {
|
||||||
|
try {
|
||||||
|
XvideosRipper ripper = new XvideosRipper(url);
|
||||||
|
ripper.rip();
|
||||||
|
assert(ripper.getWorkingDir().listFiles().length > 1);
|
||||||
|
deleteDir(ripper.getWorkingDir());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail("Error while ripping URL " + url + ": " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user