forked from I2P_Developers/i2p.i2p
* i2psnark:
- Display torrent file downloads in torrent area - Sort magnets and downloads first - Fix sorting problem when torrent dir is a symlink - Reduce max file idle time - arrow_down icon copied from console css
This commit is contained in:
BIN
apps/i2psnark/icons/arrow_down.png
Normal file
BIN
apps/i2psnark/icons/arrow_down.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 379 B |
@@ -57,7 +57,7 @@
|
|||||||
<target name="jar" depends="builddep, compile, jarUpToDate, listChangedFiles" unless="jar.uptodate" >
|
<target name="jar" depends="builddep, compile, jarUpToDate, listChangedFiles" unless="jar.uptodate" >
|
||||||
<!-- set if unset -->
|
<!-- set if unset -->
|
||||||
<property name="workspace.changes.tr" value="" />
|
<property name="workspace.changes.tr" value="" />
|
||||||
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/messages_*.class">
|
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/FetchAndAdd*.class **/messages_*.class">
|
||||||
<manifest>
|
<manifest>
|
||||||
<attribute name="Main-Class" value="org.klomp.snark.Snark" />
|
<attribute name="Main-Class" value="org.klomp.snark.Snark" />
|
||||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
|
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
<target name="jarUpToDate">
|
<target name="jarUpToDate">
|
||||||
<uptodate property="jar.uptodate" targetfile="build/i2psnark.jar" >
|
<uptodate property="jar.uptodate" targetfile="build/i2psnark.jar" >
|
||||||
<srcfiles dir= "build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/messages_*.class" />
|
<srcfiles dir= "build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/FetchAndAdd*.class **/messages_*.class" />
|
||||||
</uptodate>
|
</uptodate>
|
||||||
<condition property="shouldListChanges" >
|
<condition property="shouldListChanges" >
|
||||||
<and>
|
<and>
|
||||||
|
@@ -250,6 +250,15 @@ public class I2PSnarkUtil {
|
|||||||
|
|
||||||
public boolean connected() { return _manager != null; }
|
public boolean connected() { return _manager != null; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For FetchAndAdd
|
||||||
|
* @return null if not connected
|
||||||
|
* @since 0.9.1
|
||||||
|
*/
|
||||||
|
public I2PSocketManager getSocketManager() {
|
||||||
|
return _manager;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the destination itself
|
* Destroy the destination itself
|
||||||
*/
|
*/
|
||||||
@@ -310,7 +319,7 @@ public class I2PSnarkUtil {
|
|||||||
// we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms...
|
// we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms...
|
||||||
out = SecureFile.createTempFile("i2psnark", null, _tmpDir);
|
out = SecureFile.createTempFile("i2psnark", null, _tmpDir);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
ioe.printStackTrace();
|
_log.error("temp file error", ioe);
|
||||||
if (out != null)
|
if (out != null)
|
||||||
out.delete();
|
out.delete();
|
||||||
return null;
|
return null;
|
||||||
|
@@ -346,6 +346,8 @@ public class Snark
|
|||||||
in = new FileInputStream(f);
|
in = new FileInputStream(f);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/**** No, we don't ever fetch a torrent file this way
|
||||||
|
and we don't want to block in the constructor
|
||||||
activity = "Getting torrent";
|
activity = "Getting torrent";
|
||||||
File torrentFile = _util.get(torrent, 3);
|
File torrentFile = _util.get(torrent, 3);
|
||||||
if (torrentFile == null) {
|
if (torrentFile == null) {
|
||||||
@@ -355,6 +357,8 @@ public class Snark
|
|||||||
torrentFile.deleteOnExit();
|
torrentFile.deleteOnExit();
|
||||||
in = new FileInputStream(torrentFile);
|
in = new FileInputStream(torrentFile);
|
||||||
}
|
}
|
||||||
|
*****/
|
||||||
|
throw new IOException("not found");
|
||||||
}
|
}
|
||||||
meta = new MetaInfo(in);
|
meta = new MetaInfo(in);
|
||||||
infoHash = meta.getInfoHash();
|
infoHash = meta.getInfoHash();
|
||||||
|
@@ -598,7 +598,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ot null ok, default is none
|
* @param pt null ok, default is none
|
||||||
* @since 0.9.1
|
* @since 0.9.1
|
||||||
*/
|
*/
|
||||||
public void savePrivateTrackers(List<String> pt) {
|
public void savePrivateTrackers(List<String> pt) {
|
||||||
@@ -881,6 +881,29 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
_magnets.remove(snark.getName());
|
_magnets.remove(snark.getName());
|
||||||
removeMagnetStatus(snark.getInfoHash());
|
removeMagnetStatus(snark.getInfoHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add and start a FetchAndAdd task.
|
||||||
|
* Remove it with deleteMagnet().
|
||||||
|
*
|
||||||
|
* @param torrent must be instanceof FetchAndAdd
|
||||||
|
* @throws RuntimeException via Snark.fatal()?
|
||||||
|
* @since 0.9.1
|
||||||
|
*/
|
||||||
|
public void addDownloader(Snark torrent) {
|
||||||
|
synchronized (_snarks) {
|
||||||
|
Snark snark = getTorrentByInfoHash(torrent.getInfoHash());
|
||||||
|
if (snark != null) {
|
||||||
|
addMessage(_("Download already running: {0}", snark.getBaseName()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String name = torrent.getName();
|
||||||
|
// Tell the dir monitor not to delete us
|
||||||
|
_magnets.add(name);
|
||||||
|
_snarks.put(name, torrent);
|
||||||
|
}
|
||||||
|
torrent.startTorrent();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a torrent from a MetaInfo. Save the MetaInfo data to filename.
|
* Add a torrent from a MetaInfo. Save the MetaInfo data to filename.
|
||||||
@@ -1399,7 +1422,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
byte[] ih = Base64.decode(b64);
|
byte[] ih = Base64.decode(b64);
|
||||||
// ignore value - TODO put tracker URL in value
|
// ignore value - TODO put tracker URL in value
|
||||||
if (ih != null && ih.length == 20)
|
if (ih != null && ih.length == 20)
|
||||||
addMagnet("Magnet: " + I2PSnarkUtil.toHex(ih), ih, null, false);
|
addMagnet("* " + _("Magnet") + ' ' + I2PSnarkUtil.toHex(ih), ih, null, false);
|
||||||
// else remove from config?
|
// else remove from config?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1071,7 +1071,7 @@ public class Storage
|
|||||||
/**
|
/**
|
||||||
* Close unused RAFs - call periodically
|
* Close unused RAFs - call periodically
|
||||||
*/
|
*/
|
||||||
private static final long RAFCloseDelay = 7*60*1000;
|
private static final long RAFCloseDelay = 4*60*1000;
|
||||||
public void cleanRAFs() {
|
public void cleanRAFs() {
|
||||||
long cutoff = System.currentTimeMillis() - RAFCloseDelay;
|
long cutoff = System.currentTimeMillis() - RAFCloseDelay;
|
||||||
for (int i = 0; i < RAFlock.length; i++) {
|
for (int i = 0; i < RAFlock.length; i++) {
|
||||||
|
346
apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java
Normal file
346
apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
package org.klomp.snark.web;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.client.streaming.I2PSocketEepGet;
|
||||||
|
import net.i2p.client.streaming.I2PSocketManager;
|
||||||
|
import net.i2p.crypto.SHA1;
|
||||||
|
import net.i2p.util.EepGet;
|
||||||
|
import net.i2p.util.I2PAppThread;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SecureFile;
|
||||||
|
|
||||||
|
import org.klomp.snark.MetaInfo;
|
||||||
|
import org.klomp.snark.Snark;
|
||||||
|
import org.klomp.snark.SnarkManager;
|
||||||
|
import org.klomp.snark.Storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cancellable torrent file downloader.
|
||||||
|
* We extend Snark so its status may be easily listed in the
|
||||||
|
* web table without adding a lot of code there.
|
||||||
|
*
|
||||||
|
* Upon successful download, this Snark will be deleted and
|
||||||
|
* a "real" Snark created.
|
||||||
|
*
|
||||||
|
* The methods return values similar to a Snark in magnet mode.
|
||||||
|
* A fake info hash, which is the SHA1 of the URL, is returned
|
||||||
|
* to prevent duplicates.
|
||||||
|
*
|
||||||
|
* This Snark may be stopped and restarted, although a partially
|
||||||
|
* downloaded file is discarded.
|
||||||
|
*
|
||||||
|
* @since 0.9.1 Moved from I2PSnarkUtil
|
||||||
|
*/
|
||||||
|
public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnable {
|
||||||
|
|
||||||
|
private final I2PAppContext _ctx;
|
||||||
|
private final Log _log;
|
||||||
|
private final SnarkManager _mgr;
|
||||||
|
private final String _url;
|
||||||
|
private final byte[] _fakeHash;
|
||||||
|
private final String _name;
|
||||||
|
private volatile long _remaining = -1;
|
||||||
|
private volatile long _total = -1;
|
||||||
|
private volatile long _transferred;
|
||||||
|
private volatile boolean _isRunning;
|
||||||
|
private volatile boolean _active;
|
||||||
|
private volatile long _started;
|
||||||
|
private String _failCause;
|
||||||
|
private Thread _thread;
|
||||||
|
private EepGet _eepGet;
|
||||||
|
|
||||||
|
private static final int RETRIES = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller should call _mgr.addDownloader(this), which
|
||||||
|
* will start things off.
|
||||||
|
*/
|
||||||
|
public FetchAndAdd(I2PAppContext ctx, SnarkManager mgr, String url) {
|
||||||
|
// magnet constructor
|
||||||
|
super(mgr.util(), "Torrent download",
|
||||||
|
null, null, null, null, null, false, null);
|
||||||
|
_ctx = ctx;
|
||||||
|
_log = ctx.logManager().getLog(FetchAndAdd.class);
|
||||||
|
_mgr = mgr;
|
||||||
|
_url = url;
|
||||||
|
_name = "* " + _("Download torrent file from {0}", url);
|
||||||
|
byte[] fake = null;
|
||||||
|
try {
|
||||||
|
fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1"));
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
_fakeHash = fake;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set off by startTorrent()
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
_mgr.addMessage(_("Fetching {0}", urlify(_url)));
|
||||||
|
File file = get();
|
||||||
|
if (!_isRunning) // stopped?
|
||||||
|
return;
|
||||||
|
_isRunning = false;
|
||||||
|
if (file != null && file.exists() && file.length() > 0) {
|
||||||
|
// remove this in snarks
|
||||||
|
_mgr.deleteMagnet(this);
|
||||||
|
add(file);
|
||||||
|
} else {
|
||||||
|
_mgr.addMessage(_("Torrent was not retrieved from {0}", urlify(_url)) +
|
||||||
|
((_failCause != null) ? (": " + _failCause) : ""));
|
||||||
|
}
|
||||||
|
if (file != null)
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied from I2PSnarkUtil so we may add ourselves as a status listener
|
||||||
|
* @return null on failure
|
||||||
|
*/
|
||||||
|
private File get() {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Fetching [" + _url + "]");
|
||||||
|
File out = null;
|
||||||
|
try {
|
||||||
|
out = SecureFile.createTempFile("torrentFile", null, _mgr.util().getTempDir());
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.error("temp file error", ioe);
|
||||||
|
_mgr.addMessage("Temp file error: " + ioe);
|
||||||
|
if (out != null)
|
||||||
|
out.delete();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
out.deleteOnExit();
|
||||||
|
|
||||||
|
if (!_mgr.util().connected()) {
|
||||||
|
_mgr.addMessage(_("Opening the I2P tunnel"));
|
||||||
|
if (!_mgr.util().connect())
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
I2PSocketManager manager = _mgr.util().getSocketManager();
|
||||||
|
if (manager == null)
|
||||||
|
return null;
|
||||||
|
_eepGet = new I2PSocketEepGet(_ctx, manager, RETRIES, out.getAbsolutePath(), _url);
|
||||||
|
_eepGet.addStatusListener(this);
|
||||||
|
if (_eepGet.fetch()) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Fetch successful [" + _url + "]: size=" + out.length());
|
||||||
|
return out;
|
||||||
|
} else {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Fetch failed [" + _url + ']');
|
||||||
|
out.delete();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell SnarkManager to copy the torrent file over and add it to the Snarks list.
|
||||||
|
* This Snark may then be deleted.
|
||||||
|
*/
|
||||||
|
private void add(File file) {
|
||||||
|
_mgr.addMessage(_("Torrent fetched from {0}", urlify(_url)));
|
||||||
|
FileInputStream in = null;
|
||||||
|
try {
|
||||||
|
in = new FileInputStream(file);
|
||||||
|
byte[] fileInfoHash = new byte[20];
|
||||||
|
String name = MetaInfo.getNameAndInfoHash(in, fileInfoHash);
|
||||||
|
try { in.close(); } catch (IOException ioe) {}
|
||||||
|
Snark snark = _mgr.getTorrentByInfoHash(fileInfoHash);
|
||||||
|
if (snark != null) {
|
||||||
|
_mgr.addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = Storage.filterName(name);
|
||||||
|
name = name + ".torrent";
|
||||||
|
File torrentFile = new File(_mgr.getDataDir(), name);
|
||||||
|
|
||||||
|
String canonical = torrentFile.getCanonicalPath();
|
||||||
|
|
||||||
|
if (torrentFile.exists()) {
|
||||||
|
if (_mgr.getTorrent(canonical) != null)
|
||||||
|
_mgr.addMessage(_("Torrent already running: {0}", name));
|
||||||
|
else
|
||||||
|
_mgr.addMessage(_("Torrent already in the queue: {0}", name));
|
||||||
|
} else {
|
||||||
|
// This may take a LONG time to create the storage.
|
||||||
|
_mgr.copyAndAddTorrent(file, canonical);
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_mgr.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage());
|
||||||
|
} catch (OutOfMemoryError oom) {
|
||||||
|
_mgr.addMessage(_("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + oom.getMessage());
|
||||||
|
} finally {
|
||||||
|
try { if (in != null) in.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snark overrides so all the buttons and stats on the web page work
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void startTorrent() {
|
||||||
|
if (_isRunning)
|
||||||
|
return;
|
||||||
|
// reset counters in case starting a second time
|
||||||
|
_remaining = -1;
|
||||||
|
// leave the total if we knew it before
|
||||||
|
//_total = -1;
|
||||||
|
_transferred = 0;
|
||||||
|
_failCause = null;
|
||||||
|
_started = _ctx.clock().now();
|
||||||
|
_isRunning = true;
|
||||||
|
_active = false;
|
||||||
|
_thread = new I2PAppThread(this, "Torrent File EepGet", true);
|
||||||
|
_thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void stopTorrent() {
|
||||||
|
if (_thread != null && _isRunning) {
|
||||||
|
if (_eepGet != null)
|
||||||
|
_eepGet.stopFetching();
|
||||||
|
_thread.interrupt();
|
||||||
|
}
|
||||||
|
_isRunning = false;
|
||||||
|
_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStopped() {
|
||||||
|
return !_isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseName() {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getInfoHash() {
|
||||||
|
return _fakeHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return torrent file size or -1
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getTotalLength() {
|
||||||
|
return _total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return torrent file bytes remaining or -1
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getRemainingLength() {
|
||||||
|
return _remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return torrent file bytes remaining or -1
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getNeededLength() {
|
||||||
|
return _remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDownloadRate() {
|
||||||
|
if (_isRunning && _active) {
|
||||||
|
long time = _ctx.clock().now() - _started;
|
||||||
|
if (time > 1000) {
|
||||||
|
long rv = (_transferred * 1000) / time;
|
||||||
|
if (rv >= 100)
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDownloaded() {
|
||||||
|
return _total - _remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPeerCount() {
|
||||||
|
return (_isRunning && _active && _transferred > 0) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTrackerSeenPeers() {
|
||||||
|
return (_transferred > 0) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End Snark overrides
|
||||||
|
|
||||||
|
// EepGet status listeners to maintain the state for the web page
|
||||||
|
|
||||||
|
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
|
||||||
|
if (bytesRemaining >= 0) {
|
||||||
|
_remaining = bytesRemaining;
|
||||||
|
}
|
||||||
|
_transferred = bytesTransferred;
|
||||||
|
if (cause != null)
|
||||||
|
_failCause = cause.toString();
|
||||||
|
_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
|
||||||
|
if (bytesRemaining >= 0) {
|
||||||
|
_remaining = bytesRemaining;
|
||||||
|
_total = bytesRemaining + currentWrite + alreadyTransferred;
|
||||||
|
}
|
||||||
|
_transferred = bytesTransferred;
|
||||||
|
_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
|
||||||
|
if (bytesRemaining >= 0) {
|
||||||
|
_remaining = bytesRemaining;
|
||||||
|
_total = bytesRemaining + alreadyTransferred;
|
||||||
|
}
|
||||||
|
_transferred = bytesTransferred;
|
||||||
|
_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
|
||||||
|
if (bytesRemaining >= 0) {
|
||||||
|
_remaining = bytesRemaining;
|
||||||
|
}
|
||||||
|
_transferred = bytesTransferred;
|
||||||
|
_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void headerReceived(String url, int attemptNum, String key, String val) {}
|
||||||
|
|
||||||
|
public void attempting(String url) {}
|
||||||
|
|
||||||
|
// End of EepGet status listeners
|
||||||
|
|
||||||
|
private String _(String s) {
|
||||||
|
return _mgr.util().getString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String _(String s, String o) {
|
||||||
|
return _mgr.util().getString(s, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String urlify(String s) {
|
||||||
|
return I2PSnarkServlet.urlify(s);
|
||||||
|
}
|
||||||
|
}
|
@@ -540,11 +540,8 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
*****/
|
*****/
|
||||||
if (newURL != null) {
|
if (newURL != null) {
|
||||||
if (newURL.startsWith("http://")) {
|
if (newURL.startsWith("http://")) {
|
||||||
if (!_manager.util().connected())
|
FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL);
|
||||||
_manager.addMessage(_("Opening the I2P tunnel"));
|
_manager.addDownloader(fetch);
|
||||||
_manager.addMessage(_("Fetching {0}", urlify(newURL)));
|
|
||||||
I2PAppThread fetch = new I2PAppThread(new FetchAndAdd(_manager, newURL), "Fetch and add", true);
|
|
||||||
fetch.start();
|
|
||||||
} else if (newURL.startsWith(MAGNET) || newURL.startsWith(MAGGOT)) {
|
} else if (newURL.startsWith(MAGNET) || newURL.startsWith(MAGGOT)) {
|
||||||
addMagnet(newURL);
|
addMagnet(newURL);
|
||||||
} else if (newURL.length() == 40 && newURL.replaceAll("[a-fA-F0-9]", "").length() == 0) {
|
} else if (newURL.length() == 40 && newURL.replaceAll("[a-fA-F0-9]", "").length() == 0) {
|
||||||
@@ -880,7 +877,17 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
*/
|
*/
|
||||||
private class TorrentNameComparator implements Comparator<String> {
|
private class TorrentNameComparator implements Comparator<String> {
|
||||||
private final Comparator collator = Collator.getInstance();
|
private final Comparator collator = Collator.getInstance();
|
||||||
private final String skip = _manager.getDataDir().getAbsolutePath() + File.separator;
|
private final String skip;
|
||||||
|
|
||||||
|
public TorrentNameComparator() {
|
||||||
|
String s;
|
||||||
|
try {
|
||||||
|
s = _manager.getDataDir().getCanonicalPath();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
s = _manager.getDataDir().getAbsolutePath();
|
||||||
|
}
|
||||||
|
skip = s + File.separator;
|
||||||
|
}
|
||||||
|
|
||||||
public int compare(String l, String r) {
|
public int compare(String l, String r) {
|
||||||
if (l.startsWith(skip))
|
if (l.startsWith(skip))
|
||||||
@@ -916,8 +923,12 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers,
|
private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers,
|
||||||
boolean isDegraded, boolean noThinsp, boolean showDebug) throws IOException {
|
boolean isDegraded, boolean noThinsp, boolean showDebug) throws IOException {
|
||||||
String filename = snark.getName();
|
String filename = snark.getName();
|
||||||
File f = new File(filename);
|
if (snark.getMetaInfo() != null) {
|
||||||
filename = f.getName(); // the torrent may be the canonical name, so lets just grab the local name
|
// Only do this if not a magnet or torrent download
|
||||||
|
// Strip full path down to the local name
|
||||||
|
File f = new File(filename);
|
||||||
|
filename = f.getName();
|
||||||
|
}
|
||||||
int i = filename.lastIndexOf(".torrent");
|
int i = filename.lastIndexOf(".torrent");
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
filename = filename.substring(0, i);
|
filename = filename.substring(0, i);
|
||||||
@@ -1076,6 +1087,8 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
icon = "folder";
|
icon = "folder";
|
||||||
else if (isValid)
|
else if (isValid)
|
||||||
icon = toIcon(meta.getName());
|
icon = toIcon(meta.getName());
|
||||||
|
else if (snark instanceof FetchAndAdd)
|
||||||
|
icon = "arrow_down";
|
||||||
else
|
else
|
||||||
icon = "magnet";
|
icon = "magnet";
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
@@ -1724,7 +1737,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
}
|
}
|
||||||
ihash = xt.substring("urn:btih:".length());
|
ihash = xt.substring("urn:btih:".length());
|
||||||
trackerURL = getTrackerParam(url);
|
trackerURL = getTrackerParam(url);
|
||||||
name = "Magnet " + ihash;
|
name = "* " + _("Magnet") + ' ' + ihash;
|
||||||
String dn = getParam("dn", url);
|
String dn = getParam("dn", url);
|
||||||
if (dn != null)
|
if (dn != null)
|
||||||
name += " (" + Storage.filterName(dn) + ')';
|
name += " (" + Storage.filterName(dn) + ')';
|
||||||
@@ -1734,7 +1747,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
int col = ihash.indexOf(':');
|
int col = ihash.indexOf(':');
|
||||||
if (col >= 0)
|
if (col >= 0)
|
||||||
ihash = ihash.substring(0, col);
|
ihash = ihash.substring(0, col);
|
||||||
name = "Maggot " + ihash;
|
name = "* " + _("Magnet") + ' ' + ihash;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1941,7 +1954,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @since 0.7.14 */
|
/** @since 0.7.14 */
|
||||||
private static String urlify(String s) {
|
static String urlify(String s) {
|
||||||
return urlify(s, 100);
|
return urlify(s, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2357,83 +2370,4 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
snark.updatePiecePriorities();
|
snark.updatePiecePriorities();
|
||||||
_manager.saveTorrentStatus(snark.getMetaInfo(), storage.getBitField(), storage.getFilePriorities());
|
_manager.saveTorrentStatus(snark.getMetaInfo(), storage.getBitField(), storage.getFilePriorities());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** inner class, don't bother reindenting */
|
|
||||||
private static class FetchAndAdd implements Runnable {
|
|
||||||
private SnarkManager _manager;
|
|
||||||
private String _url;
|
|
||||||
public FetchAndAdd(SnarkManager mgr, String url) {
|
|
||||||
_manager = mgr;
|
|
||||||
_url = url;
|
|
||||||
}
|
|
||||||
public void run() {
|
|
||||||
_url = _url.trim();
|
|
||||||
// 3 retries
|
|
||||||
File file = _manager.util().get(_url, false, 3);
|
|
||||||
try {
|
|
||||||
if ( (file != null) && (file.exists()) && (file.length() > 0) ) {
|
|
||||||
_manager.addMessage(_("Torrent fetched from {0}", urlify(_url)));
|
|
||||||
FileInputStream in = null;
|
|
||||||
try {
|
|
||||||
in = new FileInputStream(file);
|
|
||||||
byte[] fileInfoHash = new byte[20];
|
|
||||||
String name = MetaInfo.getNameAndInfoHash(in, fileInfoHash);
|
|
||||||
try { in.close(); } catch (IOException ioe) {}
|
|
||||||
Snark snark = _manager.getTorrentByInfoHash(fileInfoHash);
|
|
||||||
if (snark != null) {
|
|
||||||
_manager.addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = Storage.filterName(name);
|
|
||||||
name = name + ".torrent";
|
|
||||||
File torrentFile = new File(_manager.getDataDir(), name);
|
|
||||||
|
|
||||||
String canonical = torrentFile.getCanonicalPath();
|
|
||||||
|
|
||||||
if (torrentFile.exists()) {
|
|
||||||
if (_manager.getTorrent(canonical) != null)
|
|
||||||
_manager.addMessage(_("Torrent already running: {0}", name));
|
|
||||||
else
|
|
||||||
_manager.addMessage(_("Torrent already in the queue: {0}", name));
|
|
||||||
} else {
|
|
||||||
// This may take a LONG time to create the storage.
|
|
||||||
_manager.copyAndAddTorrent(file, canonical);
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_manager.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage());
|
|
||||||
} catch (OutOfMemoryError oom) {
|
|
||||||
_manager.addMessage(_("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + oom.getMessage());
|
|
||||||
} finally {
|
|
||||||
try { if (in != null) in.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Generate a retry link, but sadly can't have a form inside a table
|
|
||||||
// So make this an ugly GET
|
|
||||||
StringBuilder buf = new StringBuilder(1024);
|
|
||||||
// FIXME don't lose peer setting
|
|
||||||
//String peerParam = req.getParameter("p");
|
|
||||||
//if (peerParam != null)
|
|
||||||
// buf.append("<input type=\"hidden\" name=\"p\" value=\"").append(peerParam).append("\" >\n");
|
|
||||||
buf.append(_("Torrent was not retrieved from {0}", urlify(_url)));
|
|
||||||
/**** FIXME ticket #575
|
|
||||||
String link = urlEncode(_url).replace(":", "%3A").replace("/", "%2F");
|
|
||||||
buf.append(" - [<a href=\"/i2psnark/?newURL=").append(link).append("#add\" >");
|
|
||||||
buf.append(_("Retry"));
|
|
||||||
buf.append("</a>]");
|
|
||||||
****/
|
|
||||||
_manager.addMessage(buf.toString());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (file != null) file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String _(String s, String o) {
|
|
||||||
return _manager.util().getString(s, o);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,11 @@
|
|||||||
|
2012-06-11 zzz
|
||||||
|
* i2psnark:
|
||||||
|
- Display torrent file downloads in torrent area
|
||||||
|
- Sort magnets and downloads first
|
||||||
|
- Fix sorting problem when torrent dir is a symlink
|
||||||
|
- Reduce max file idle time
|
||||||
|
* NativeBigInteger: Workaround for Raspberry Pi to load the correct lib
|
||||||
|
|
||||||
2012-06-08 zzz
|
2012-06-08 zzz
|
||||||
* i2psnark:
|
* i2psnark:
|
||||||
- Move private tracker config from create box to torrent config
|
- Move private tracker config from create box to torrent config
|
||||||
|
@@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 11;
|
public final static long BUILD = 12;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user