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 javax.net.ssl.HttpsURLConnection; 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.downloadExists(url, saveAs); 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.debug("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 * 256]; int bytesRead; try { logger.info(" Downloading file: " + url + (tries > 0 ? " Retry #" + tries : "")); observer.sendUpdate(STATUS.DOWNLOAD_STARTED, url.toExternalForm()); // Setup HTTP request HttpURLConnection huc; if (this.url.toString().startsWith("https")) { huc = (HttpsURLConnection) this.url.openConnection(); } else { huc = (HttpURLConnection) this.url.openConnection(); } huc.setInstanceFollowRedirects(true); huc.setConnectTimeout(0); // Never timeout huc.setRequestProperty("accept", "*/*"); huc.setRequestProperty("Referer", this.url.toExternalForm()); // Sic huc.setRequestProperty("User-agent", AbstractRipper.USER_AGENT); tries += 1; logger.debug("Request properties: " + huc.getRequestProperties().toString()); huc.connect(); // Check status code bis = new BufferedInputStream(huc.getInputStream()); 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"); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("Referer", this.url.toExternalForm()); // Sic conn.setRequestProperty("User-agent", AbstractRipper.USER_AGENT); return conn.getContentLength(); } }