MoparScape/MoparScape/src/main/java/org/moparscape/res/ResourceGrabber.java

986 lines
42 KiB
Java

/*
* Copyright (C) 2009-2013 moparisthebest
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Official forums are http://www.moparscape.org/smf/
* Email me at admin@moparisthebest.com.
*/
package org.moparscape.res;
import org.moparscape.Debug;
import org.moparscape.res.impl.BTDownloader;
import org.moparscape.res.impl.Downloader;
import org.moparscape.res.impl.URLDownloader;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This class is meant to retrieve resources from a variety of URLs, including all supported by Java in addition to
* magnet links, and torrent files both locally and at http and https URLs.
* <p/>
* Torrent support is provided through the native java_client powered by libtorrent-rasterbar, the correct executable
* for your platform is automatically checked for validity and downloaded via HTTP if need be.
* <p/>
* This class will refuse to download or extract files of certain extensions, currently including:
* (exe|bat|cmd|com|sh|bash)
* This is for security reasons. The only exception to this rule is it will download java_client.exe for internal
* purposes, but will only run it if the CRC is correct.
* <p/>
* This class should be thread-safe.
* <p/>
* Only one instance of this class needs to be instantiated per-VM. Currently this is enforced via the constructors.
*
* @author moparisthebest
*/
public class ResourceGrabber {
private static final String fileListFile = "filesToCheck.txt";
private static ResourceGrabber _instance = null;
private final Downloader[] downloaders;
private static final int delay = 1000; //milliseconds
private static final int errorTicks = 60; // errorTicks * delay is how long errors will stay onscreen
private JFrame frame = null;
private javax.swing.Timer timer = null;
private String title = "Resource Grabber";
private final List<DlListener> downloadItems = new ArrayList<DlListener>(5);
// this is only meant to be accessed by getNewUID(), which is synchronized
private int currentUID = 0;
public static void main1(String[] args) throws Exception {
if (args.length == 0 || (args.length % 4) != 0) {
System.out.println("Usage: ResourceGrabber [[TORRENT|MAGNETURL|URL] SAVE_PATH EXTRACT CRC]\n" +
"TORRENT is a path to a .torrent file\n" +
"MAGNETURL is a magnet link\n" +
"URL is a url to a torrent file\n" +
"SAVE_PATH is the absolute path to save the torrent and .resume file to\n" +
"EXTRACT is true if you wish to extract the downloaded file\n" +
"CRC if not 0 will only download the file if the CRC does not match, it will checksum every file in the save_path recursively.\n\n" +
"You can specify as many downloads on the command line as you wish.");
return;
}
ResourceGrabber rg = getResourceGrabber(System.getProperty("user.home") + "/.moparscape/bin/");
int[] uids = new int[args.length / 4];
for (int x = 0; x < uids.length; ++x) {
int argIndex = x * 4;
String url = args[argIndex];
String savePath = args[argIndex + 1];
boolean extract = args[argIndex + 2].equalsIgnoreCase("true");
long crc = 0;
try {
crc = Long.parseLong(args[argIndex + 3]);
} catch (NumberFormatException e) {
}
System.out.println(String.format("Starting download %d, url: '%s' savePath: '%s' extract: '%b' crc: '%d'",
x + 1, url, savePath, extract, crc));
uids[x] = rg.download(url, savePath, extract, crc == 0 ? null : new ChecksumInfo(crc));
System.out.println("Started download " + (x + 1));
}
System.out.println("Started all downloads, now waiting until they all finish!");
for (int x = 0; x < uids.length; ++x) {
boolean result = false;
try {
result = rg.wait(uids[x]);
} catch (Exception e) {
System.out.println(String.format("Download %d failed, here is the exception:", x + 1));
e.printStackTrace();
continue;
}
if (uids[x] == -1)
System.out.println(String.format("Download %d finished instantly, CRC must have matched!", x + 1));
else if (result)
System.out.println(String.format("Download %d finished successfully!", x + 1));
else
System.out.println(String.format("Download %d failed, don't know why...", x + 1));
}
System.out.println("All downloads are finished, exiting program...");
}
public static void main(String[] args) throws Exception {
//downloadHTTP("https://www.moparscape.org/libs/client.zip.gz", "/home/mopar/tests/extest");
//extractFile("/home/mopar/tests/extest/client.zip.gz", "/home/mopar/tests/extest/");
//download("https://www.moparscape.org/libs/client.zip.gz", "/home/mopar/tests/extest", true, 98233333, new String[]{"client_test.linux.x86", "client_test.osx.i386"});
//download("https://www.moparscape.org/libs/client.zip.gz", "/home/mopar/tests/extest", true, 98233333, new String[]{"combined"});
/*System.out.println("checksum: " + Downloader.checksum("/home/mopar/tests/extest", null, null, true));
System.out.println("checksum: " + Downloader.checksum("/home/mopar/tests/extest", new Adler32(), null, true));
System.out.println("checksum: " + Downloader.checksum("/home/mopar/tests/extest", null, new String[]{"client_test.linux.x86", "client.zip.gz"}, true));
System.out.println("checksum: " + Downloader.checksum("/home/mopar/tests/extest", null, new String[]{"client_test.linux.x86", "client.zip.gz"}, false));
*/
//System.out.println("filename: " + new URL("http://moparisthebest.com/bob/tom/cache.zip").getFile());
/*String s = "result: x";
for(String p : s.split(":"))
System.out.println("part: '"+p.trim()+"'");
if(true) return; */
ResourceGrabber rg = getResourceGrabber(System.getProperty("user.home") + "/.moparscape/bin/");
System.out.println("before downloads...");
rg.download("http://www.moparisthebest.com/downloads/cedegaSRC.tar.gz", "/home/mopar/tests/extest", true, null, true, new CompleteRunnable() {
@Override
public void run() {
System.out.println("download complete bitches! uid: " + this.uid + " exception: " + this.ex);
}
});/*
int clientZipUID = -1;
try {
//rg.download("http://www.moparisthebest.com/downloads/cedegaSRC.tar.gz", "/home/mopar/tests/extest", true);
//rg.download("http://mirror01.th.ifl.net/releases//maverick/ubuntu-10.10-desktop-i386.iso", "/home/mopar/tests/extest", false);
//Thread.sleep(30000);
//int clientZipUID = rg.download("https://www.moparscape.org/libs/client.zip.gz", "/home/mopar/tests/extest", true);
//int clientZipUID = rg.download("https://www.moparscape.org/libs/client.zip.torrent", "/home/mopar/tests/extest", true);
//int clientZipUID = rg.download("magnet:?xt=urn:btih:a38d02c287893842a32825aa866e00828a318f07&dn=Ubuntu+11.04+%28Final%29&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.ccc.de%3A80", "/home/mopar/tests/extest");
clientZipUID = rg.download("magnet:?xt=urn:btih:87171cac4b10b28e8ceb00df18a883bbf3363fca&dn=House+S07E23+Moving+On+HDTV+XviD-2HD+%5Beztv%5D&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.ccc.de%3A80", "/home/mopar/tests/extest");
} catch (Exception e) {
}
//int clientZipUID = rg.download("magnet:?xt=urn:btih:CDXN5L2YV5FLXVL36GKUTRXIQDOUDKDY&dn=client.zip&tr=udp%3a%2f%2ftracker.openbittorrent.com%3a80", "/home/mopar/tests/dldir");
System.out.println("returned: '" + rg.wait(clientZipUID) + "' after downloads..."); */
}
private ResourceGrabber(String binDir, String title) throws FileNotFoundException {
if (title != null)
this.title = title;
File f = new File(binDir);
if (!f.exists() && !f.isDirectory() && !f.mkdirs())
throw new FileNotFoundException();
if (!binDir.endsWith("/"))
binDir += "/";
// order matters here
//downloaders = new Downloader[]{new BTDownloader(binDir), new URLDownloader()};
downloaders = new Downloader[]{new URLDownloader()};
}
private ResourceGrabber(String binDir) throws FileNotFoundException {
this(binDir, null);
}
public synchronized static ResourceGrabber getResourceGrabber(String binDir, String title) throws FileNotFoundException {
if (_instance == null)
_instance = new ResourceGrabber(binDir, title);
return _instance;
}
public synchronized static ResourceGrabber getResourceGrabber() throws IllegalStateException {
if (_instance == null)
throw new IllegalStateException("Must call getResourceGrabber method with parameters first.");
return _instance;
}
public synchronized static void destroy(){
if(_instance == null)
return;
synchronized (_instance.downloadItems){
for (final DlListener dll : _instance.downloadItems) {
dll.autoRemove = true;
dll.setStopped();
}
}
for(Downloader d : _instance.downloaders)
d.destroy();
_instance = null;
}
public static ResourceGrabber getRG() throws IllegalStateException {
return getResourceGrabber();
}
public static ResourceGrabber getResourceGrabber(String binDir) throws FileNotFoundException {
return getResourceGrabber(binDir, null);
}
public boolean waitCatch(int uid) {
return waitCatch(uid, 0L);
}
public boolean waitCatch(int uid, long timeout) {
return waitCatch(uid, timeout, false);
}
public boolean waitCatch(int uid, boolean freeResults) {
return waitCatch(uid, 0L, freeResults);
}
public boolean waitCatch(int uid, long timeout, boolean freeResults) {
try {
return this.wait(uid, timeout, freeResults);
} catch (Exception e) {
Debug.debug(e);
return false;
}
}
public boolean wait(int uid) throws Exception {
return wait(uid, 0L);
}
public boolean wait(int uid, long timeout) throws Exception {
return wait(uid, timeout, false);
}
public boolean wait(int uid, boolean freeResults) throws Exception {
return wait(uid, 0L, freeResults);
}
private DlListener listenerForUID(int uid) {
// -1 is a special value meaning return immediately
// maybe because CRC was already correct and download not needed
if (uid == -1)
return null;
// grab the listener for this uid
synchronized (downloadItems) {
for (final DlListener d : downloadItems)
if (d.uid == uid)
return d;
}
return null;
}
public boolean wait(int uid, long timeout, boolean freeResults) throws Exception {
DlListener dll = this.listenerForUID(uid);
// if we couldn't find one, the download is finished, return
if (dll == null)
return true; // we don't really know how it ended, just return true I guess...
DownloadListener.Status status = dll.getStatus();
long startTime = timeout > 0L ? System.currentTimeMillis() : 0L;
long elapsedTime;
while (status != AbstractDownloadListener.Status.FINISHED
&& status != AbstractDownloadListener.Status.STOPPED
&& status != AbstractDownloadListener.Status.ERROR) {
try {
synchronized (dll) {
//dll.wait(timeout);
// todo: waiting for 0 (forever) sometimes locks up because of a race condition, make it smaller
//dll.wait(2000);
// maybe this will work instead, now that we are synchronized around dll
status = dll.getStatus();
if ((status != AbstractDownloadListener.Status.FINISHED
&& status != AbstractDownloadListener.Status.STOPPED
&& status != AbstractDownloadListener.Status.ERROR))
dll.wait(timeout);
}
} catch (InterruptedException e) {
// just ignore it, let the loop go around again
}
status = dll.getStatus();
if (timeout > 0L) {
elapsedTime = System.currentTimeMillis() - startTime;
// check and make sure we haven't gone over our allotted time
if (elapsedTime >= timeout) {
if (freeResults)
dll.autoRemove = true;
return status == AbstractDownloadListener.Status.FINISHED
|| status == AbstractDownloadListener.Status.STOPPED;
} else
timeout -= elapsedTime; // if not, adjust timeout accordingly
}
}
if (freeResults)
dll.autoRemove = true;
if (dll.exception != null)
throw dll.exception;
return status != AbstractDownloadListener.Status.ERROR;
}
public int download(String url, String savePath) throws MalformedURLException {
return this.download(url, savePath, false, null);
}
public int download(String url, String savePath, boolean extract) throws MalformedURLException {
return this.download(url, savePath, extract, null);
}
public int download(String url, String savePath, boolean extract, ChecksumInfo ci) throws MalformedURLException {
return this.download(url, savePath, extract, ci, false);
}
public int download(String url, String savePath, boolean extract, ChecksumInfo ci, Downloader blacklist) throws MalformedURLException {
return this.download(url, savePath, extract, ci, false, blacklist);
}
public String uniqueFoldername(String url) {
return this.uniqueFoldername(url, null, null);
}
public String uniqueFoldername(String url, String savePath) {
return this.uniqueFoldername(url, savePath, null);
}
public String uniqueFoldername(String url, String savePath, java.util.List<String> files) {
Downloader dlr;
try {
dlr = getSupportedDownloader(url);
} catch (MalformedURLException e) {
return null;
}
// append to savePath a unique folder name
if (savePath != null) {
if (!savePath.endsWith("/"))
savePath += "/";
savePath = savePath + dlr.uniqueFoldername(url);
} else {
return dlr.uniqueFoldername(url);
}
// if a list of files are requested, try to make the Downloader take an educated guess
if (files != null)
dlr.guessFilenames(url, savePath, files);
return savePath;
}
public int download(String url, String savePath, boolean extract, ChecksumInfo ci, boolean uniqueFolder) throws MalformedURLException {
return this.download(url, savePath, extract, ci, uniqueFolder, (Downloader)null);
}
public int download(String url, String savePath, boolean extract, ChecksumInfo ci, boolean uniqueFolder, Downloader blacklist) throws MalformedURLException {
// if a list of files are requested, try to make the Downloader take an educated guess
// also append to savePath a unique folder name
java.util.List<String> files = newThreadSafeList();
if (uniqueFolder) {
savePath = uniqueFoldername(url, savePath, files);
}
// check if file was already downloaded
File listFile = new File(savePath + fileListFile);
boolean listFileExists = listFile.exists() && listFile.canRead() && listFile.isFile();
// if this file exists, and ci is null, or expectedChecksum is 0, just return -1 (already downloaded)
//System.out.printf("url: '%s', listFileExists: '%b', ci: '%s'\n", url, listFileExists, ci);
if (listFileExists && (ci != null && ci.getExpectedChecksum() == 0))
return -1;
// check crc if we are supposed to
if (ci != null && listFileExists) {
// try to load a whitelist from when the file was first downloaded, so we can only CRC those files
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Downloader.writeStream(new FileInputStream(listFile), baos);
String[] whitelist = new String(baos.toByteArray()).split("\n");
//for(String file: whitelist) System.out.println("whitelist file: "+file);
ci.setList(true, whitelist);
} catch (Exception e) {
Debug.debug(e);
}
/*
String archive = new URL(url.startsWith("http") ? url : "file://"+url).getFile();
archive = savePath+archive.substring(archive.lastIndexOf("/")+1, archive.length());
System.out.println("crcExtractFile: "+Downloader.crcExtractFile(archive)+" archive: "+archive);
ci.checksumMatch(savePath);
System.out.println("crc: "+ci.getChecksum()+" expected: "+ci.getExpectedChecksum()+ " checksum match: "+url);
*/
if (ci.checksumMatch(savePath))
return -1; // this signifies that the crc matches (instant success)
}
// otherwise go ahead and download it.
Downloader dlr = getSupportedDownloader(url, blacklist);
int uid = getNewUID();
DlListener dll = new DlListener(uid, extract, ci, files, this);
dlr.download(url, savePath, dll);
synchronized (downloadItems)
{
downloadItems.add(dll);
if (timer == null) {
timer = new Timer(delay, new GUIUpdater());
timer.start();
} else if (!timer.isRunning()) {
timer.start();
}
}
return uid;
}
public int download(final String url, final String savePath, final boolean extract, final ChecksumInfo ci, final boolean uniqueFolder, final Runnable run) {
final ResourceGrabber rg = this;
final CompleteRunnable crun;
if (run instanceof CompleteRunnable)
crun = (CompleteRunnable) run;
else
crun = null;
int tryUid = -1;
try {
tryUid = rg.download(url, savePath, extract, ci, uniqueFolder);
} catch (Exception e) {
if (crun != null)
crun.setEx(e);
}
final int uid = tryUid;
new Thread() {
public void run() {
boolean success = false;
try {
success = rg.wait(uid, false);
} catch (Exception e) {
if (crun != null)
crun.setEx(e);
}
if (crun != null)
crun.set(rg, success, uid, url, savePath, extract, ci, uniqueFolder);
run.run();
rg.freeResources(uid);
}
}.start();
return uid;
}
public boolean downloadWait(String url, String savePath) throws Exception {
return this.downloadWait(url, savePath, false, null);
}
public boolean downloadWait(String url, String savePath, boolean extract) throws Exception {
return this.downloadWait(url, savePath, extract, null);
}
public boolean downloadWait(String url, String savePath, boolean extract, ChecksumInfo ci) throws Exception {
return this.downloadWait(url, savePath, extract, ci, false);
}
public boolean downloadWait(String url, String savePath, boolean extract, ChecksumInfo ci, boolean uniqueFolder) throws Exception {
return this.wait(this.download(url, savePath, extract, ci, uniqueFolder), true);
}
public boolean downloadWaitCatch(String url, String savePath) {
return this.downloadWaitCatch(url, savePath, false, null);
}
public boolean downloadWaitCatch(String url, String savePath, boolean extract) {
return this.downloadWaitCatch(url, savePath, extract, null);
}
public boolean downloadWaitCatch(String url, String savePath, boolean extract, ChecksumInfo ci) {
try {
return this.downloadWait(url, savePath, extract, ci);
} catch (Exception e) {
// ignore, just return false
return false;
}
}
public int downloadToUniqueFolder(String url, String parentFolder) throws MalformedURLException {
return this.downloadToUniqueFolder(url, parentFolder, false, null);
}
public int downloadToUniqueFolder(String url, String parentFolder, boolean extract) throws MalformedURLException {
return this.downloadToUniqueFolder(url, parentFolder, extract, null);
}
public int downloadToUniqueFolder(String url, String parentFolder, ChecksumInfo ci) throws MalformedURLException {
return this.downloadToUniqueFolder(url, parentFolder, false, ci);
}
public int downloadToUniqueFolder(String url, String parentFolder, boolean extract, ChecksumInfo ci) throws MalformedURLException {
return this.download(url, parentFolder, extract, ci, true);
}
public boolean downloadToUniqueFolderWait(String url, String parentFolder) throws Exception {
return this.downloadToUniqueFolderWait(url, parentFolder, false, null);
}
public boolean downloadToUniqueFolderWait(String url, String parentFolder, boolean extract) throws Exception {
return this.downloadToUniqueFolderWait(url, parentFolder, extract, null);
}
public boolean downloadToUniqueFolderWait(String url, String parentFolder, boolean extract, ChecksumInfo ci) throws Exception {
return this.wait(this.download(url, parentFolder, extract, ci, true));
}
public boolean downloadToUniqueFolderWaitCatch(String url, String parentFolder) {
return this.downloadToUniqueFolderWaitCatch(url, parentFolder, false, null);
}
public boolean downloadToUniqueFolderWaitCatch(String url, String parentFolder, boolean extract) {
return this.downloadToUniqueFolderWaitCatch(url, parentFolder, extract, null);
}
public boolean downloadToUniqueFolderWaitCatch(String url, String parentFolder, boolean extract, ChecksumInfo ci) {
try {
return this.downloadToUniqueFolderWait(url, parentFolder, extract, ci);
} catch (Exception e) {
// ignore, just return false
return false;
}
}
private synchronized int getNewUID() {
return this.currentUID++;
}
private Downloader getSupportedDownloader(String url) throws MalformedURLException {
return this.getSupportedDownloader(url, null);
}
private Downloader getSupportedDownloader(String url, Downloader blacklist) throws MalformedURLException {
for (Downloader dl : this.downloaders)
if (dl != blacklist && dl.supportsURL(url)) {
//System.out.println("url: " + url + " returning: " + dl);
return dl;
}
throw new MalformedURLException("Unsupported URL: " + url);
}
public boolean supportedURL(String url) {
try {
this.getSupportedDownloader(url);
return true;
} catch (MalformedURLException e) {
return false;
}
}
public void freeResources(int uid) {
DlListener dll = this.listenerForUID(uid);
if (dll != null)
dll.freeResources();
}
public java.util.List<String> getFileList(int uid) {
DlListener dll = this.listenerForUID(uid);
return dll == null ? null : dll.getFileList();
}
private java.util.List<String> newThreadSafeList() {
return Collections.synchronizedList(this.newList());
}
private java.util.List<String> newThreadSafeList(int initalSize) {
return Collections.synchronizedList(this.newList(initalSize));
}
private java.util.List<String> newList() {
return new ArrayList<String>();
}
private java.util.List<String> newList(int initalSize) {
return new ArrayList<String>(initalSize);
}
public String firstFileEndsWithIgnoreCase(String url, String savePath, int uid, String... suffixes) {
return this.firstFileEndsWith(url, savePath, uid, true, suffixes);
}
public String firstFileEndsWith(String url, String savePath, int uid, String... suffixes) {
return this.firstFileEndsWith(url, savePath, uid, false, suffixes);
}
public String firstFileEndsWith(String url, String savePath, int uid, boolean ignoreCase, String... suffixes) {
return this.listenerForUID(uid) != null ? this.firstFileEndsWith(uid, ignoreCase, suffixes) : this.firstFileEndsWith(url, savePath, ignoreCase, suffixes);
}
public String firstFileEndsWithIgnoreCase(String url, String savePath, String... suffixes) {
return this.firstFileEndsWith(url, savePath, true, suffixes);
}
public String firstFileEndsWith(String url, String savePath, String... suffixes) {
return this.firstFileEndsWith(url, savePath, false, suffixes);
}
public String firstFileEndsWith(String url, String savePath, boolean ignoreCase, String... suffixes) {
// no need for concurrent because only we have access to this list and it will only be accessed in a single thread
java.util.List<String> files = newList(2);
if (url != null)
this.uniqueFoldername(url, savePath, files);
else
Downloader.listFiles(savePath, files);
return this.firstFileEndsWith(files, ignoreCase, suffixes);
}
public String firstFileEndsWithIgnoreCase(int uid, String... suffixes) {
return this.firstFileEndsWith(uid, true, suffixes);
}
public String firstFileEndsWith(int uid, String... suffixes) {
return this.firstFileEndsWith(uid, false, suffixes);
}
public String firstFileEndsWith(int uid, boolean ignoreCase, String... suffixes) {
return this.firstFileEndsWith(getFileList(uid), ignoreCase, suffixes);
}
public String firstFileEndsWithIgnoreCase(java.util.List<String> files, String... suffixes) {
return this.firstFileEndsWith(files, true, suffixes);
}
public String firstFileEndsWith(java.util.List<String> files, String... suffixes) {
return this.firstFileEndsWith(files, false, suffixes);
}
public String firstFileEndsWith(java.util.List<String> files, boolean ignoreCase, String... suffixes) {
if (files == null || suffixes.length < 1)
return null;
synchronized (files) {
for (String file : files)
for (String suffix : suffixes)
if (((ignoreCase && suffix != null && file.toLowerCase().endsWith(suffix.toLowerCase())) || file.endsWith(suffix)) && !file.endsWith(fileListFile))
return file;
// if the last value in suffixes is null, that is a special meaning to return the first file if no
// others can be found that matches the previous ones
if (suffixes[suffixes.length - 1] == null)
return files.isEmpty() ? null : files.get(0);
}
return null;
}
private class GUIUpdater implements ActionListener {
public void actionPerformed(ActionEvent e) {
synchronized (downloadItems) {
for (final DlListener dll : downloadItems) {
/*
System.out.println("-------------------------------------------------------------");
System.out.println("uid: " + dll.uid);
//System.out.println("dll: " + dll);
System.out.println("-------------------------------------------------------------");
*/
DownloadListener.Status status = dll.pollStatus();
if (status == null)
status = dll.getStatus();
while (status != null) {
switch (status) {
case NOT_STARTED:
break;
case FINISHED:
case RUNNING:
// check if we have called reset
if (dll.info != null) {
dll.dip.reset(dll.title, dll.length, dll.info);
dll.info = null;
break;
}
// if its already running, we need to just update it
if (dll.title != null)
dll.dip.setTitle(dll.title);
//if (dll.progress != 0)
// dll.dip.setProgress(dll.progress);
if (dll.extraInfo != null)
dll.dip.setInfo(dll.extraInfo);
// set them all not to update
dll.title = null;
dll.extraInfo = null;
break;
// then we need to start it up
case STARTING:
//dll.setRunning();
// this means we are RE-starting for some reason, so it's already added to the frame
if (dll.dip != null) {
dll.dip.reset(dll.title, dll.length, dll.info);
dll.info = null;
break;
}
// otherwise, start fresh
dll.dip = new DownloadItemPanel(dll.title, dll.length, dll.info);
if (frame == null) {
frame = new JFrame(title);
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
frame.getContentPane().add(dll.dip);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setResizable(false);
frame.pack();
// sets the frame to appear in the middle of the screen
// when called after pack()
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// or if we are trying to add a panel and the frame is already set up
} else {
frame.getContentPane().add(dll.dip);
}
break;
case EXTRACTING:
//dll.setRunning();
if (dll.dip != null)
dll.dip.reset(dll.title, dll.length, dll.info);
break;
case STOPPED:
if (!dll.autoRemove)
break;
// since we are already in the event thread, this executes right after this exits
// or at least never at the same time, which is all we need to worry about.
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
synchronized (downloadItems) {
downloadItems.remove(dll);
if (frame == null)
return;
if (dll.dip != null)
frame.getContentPane().remove(dll.dip);
if (downloadItems.isEmpty()) {
frame.dispose();
frame = null;
timer.stop();
}
}
}
});
break;
case ERROR:
//System.out.println("Error uid: " + dll.uid);
if (dll.extraInfo != null && dll.dip != null)
dll.dip.error(dll.extraInfo);
dll.extraInfo = null;
// timeout error, once we reach errorTicks ticks change it to stopped
//System.out.println("error tick: " + dll.progress);
if (dll.autoRemove && (dll.progress++ > errorTicks))
dll.setStopped();
}
status = dll.pollStatus();
}
}
}
if (frame != null)
frame.pack();
}
}
private class DlListener extends AbstractDownloadListener {
int uid;
boolean extract;
ChecksumInfo ci;
java.util.List<String> files;
DownloadItemPanel dip = null;
ResourceGrabber rg = null;
boolean autoRemove = false;
public DlListener(int uid, boolean extract, ChecksumInfo ci, java.util.List<String> files, ResourceGrabber rg) {
this.uid = uid;
this.extract = extract;
this.ci = ci;
this.files = files;
this.rg = rg;
//this.autoRemove = (files == null);
}
@Override
public void setProgress(int progress) {
//super.setProgress(progress);
// it is safe to update the progress outside of the event thread, and it looks cleaner, so do it
if (dip != null)
dip.setProgress(progress);
}
@Override
public synchronized void finished(String savePath, String... filesDownloaded) {
if (savePath != null && !savePath.endsWith("/"))
savePath += "/";
/*
if (extract)
for (String file : filesDownloaded)
Downloader.extractFile(file, savePath, this, files);
*/
if (files != null)
files.clear();
// if we are supposed to extract it, do so, add the names to files if they extract
for (String file : filesDownloaded) {
file = savePath + file;
if (!(extract && Downloader.supportsExtraction(file) &&
Downloader.extractFile(file, savePath, this, files)) &&
files != null)
files.add(file);
}
// write files to a file in the savePath, to be used on later runs to see what to CRC
if (files != null) {
String[] fileArray = files.toArray(new String[files.size()]);
// first strip off savePath from the files
for (int x = 0; x < fileArray.length; ++x){
//fileArray[x] = fileArray[x].replaceFirst(savePath, "");
fileArray[x] = fileArray[x].substring(savePath.length(), fileArray[x].length());
//System.out.printf("stripped file: '%s'\n",fileArray[x]);
}
try {
FileOutputStream fos = new FileOutputStream(savePath + fileListFile);
for (String file : fileArray) {
file += "\n";
//System.out.print("file to crc: " + file);
fos.write(file.getBytes());
}
fos.close();
} catch (Exception e) {
Debug.debug(e);
}
if (ci != null)
ci.setList(true, fileArray);
}
// if we want a list of files, grab one
//if (files != null)
// Downloader.listFiles(savePath, files);
// check crc if we are supposed to
//System.out.println("savePath: "+savePath);
if (ci != null && ci.getExpectedChecksum() != 0 && !ci.checksumMatch(savePath))
error(String.format("CRC Mismatch. expected: %d actual: %d", ci.getExpectedChecksum(), ci.getChecksum()), null);
else
super.finished(savePath, filesDownloaded);
//System.out.println("returning from finished");
// we can at least free this now
ci = null;
}
public void error(String msg, Exception e) {
Debug.debug("Error: " + msg);
Debug.debug(e);
super.error(msg, e);
}
public synchronized void freeResources() {
ci = null;
files = null;
autoRemove = true;
}
public synchronized java.util.List<String> getFileList() {
return files;
}
public boolean download(String url, String savePath, boolean extract, ChecksumInfo ci, Downloader dl) throws Exception {
return rg.wait(rg.download(url, savePath, extract, ci, dl), true);
}
/**
* Checks equality with another Object
*
* @param other
* @return
*/
@Override
public boolean equals(Object other) {
//System.out.println("DlListener equals: " + other);
return ((other != null) && (other instanceof DlListener) && (((DlListener) other).uid == this.uid));
}
@Override
public String toString() {
return "DlListener{" +
"uid=" + uid +
", autoRemove=" + autoRemove +
", status=" + peekStatus().toString() +
", dip=" + dip +
'}';
}
}
private class DownloadItemPanel extends JPanel {
private final static String sep = "<html><hr><hr>";
private final static String end = "</html>";
JProgressBar progressBar = null;
JLabel titleLabel = null;
JLabel infoLabel = null;
String origInfo = null;
public DownloadItemPanel(String title, long length, String info) {
super(new BorderLayout());
this.add(this.titleLabel = new JLabel(sep + title + end), BorderLayout.NORTH);
this.add(this.infoLabel = new JLabel(origInfo = info), BorderLayout.SOUTH);
progressBar = new JProgressBar(0, 100);
this.setLength(length);
this.add(progressBar, BorderLayout.CENTER);
}
public void reset(final String title, final long length, final String info) {
if (title != null)
this.setTitle(title);
if (info != null)
infoLabel.setText(origInfo = info);
this.setLength(length);
}
public void error(final String error) {
this.setInfo("Error: " + error);
this.setLength(-1);
}
private void setLength(long length) {
if (length != -1) {
progressBar.setValue(0);
progressBar.setMaximum((int) length);
progressBar.setIndeterminate(false);
progressBar.setStringPainted(true);
} else {
progressBar.setIndeterminate(true);
progressBar.setStringPainted(false);
}
}
public void setProgress(final int progress) {
progressBar.setValue(progress);
}
public void setTitle(final String title) {
this.titleLabel.setText(sep + title + end);
this.titleLabel.updateUI();
}
public void setInfo(final String info) {
this.infoLabel.setText("<html>" + origInfo + "<hr>" + info + end);
}
@Override
public String toString() {
return "DownloadItemPanel{" +
"infoLabel=" + infoLabel.getText() +
", titleLabel=" + titleLabel.getText() +
", origInfo='" + origInfo + '\'' +
", progressBar=" + progressBar.getPercentComplete() +
'}';
}
}
}