forked from I2P_Developers/i2p.i2p
* i2psnark:
- Fixes when complete except for skipped files (ticket #447) status in UI, don't connect outbound, disconnect seeds when done - More classes pkg private
This commit is contained in:
@ -24,7 +24,7 @@ package org.klomp.snark;
|
|||||||
/**
|
/**
|
||||||
* Callback used when some peer changes state.
|
* Callback used when some peer changes state.
|
||||||
*/
|
*/
|
||||||
public interface CoordinatorListener
|
interface CoordinatorListener
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Called when the PeerCoordinator notices a change in the state of a peer.
|
* Called when the PeerCoordinator notices a change in the state of a peer.
|
||||||
|
@ -48,7 +48,7 @@ import org.klomp.snark.dht.DHT;
|
|||||||
/**
|
/**
|
||||||
* Coordinates what peer does what.
|
* Coordinates what peer does what.
|
||||||
*/
|
*/
|
||||||
public class PeerCoordinator implements PeerListener
|
class PeerCoordinator implements PeerListener
|
||||||
{
|
{
|
||||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerCoordinator.class);
|
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerCoordinator.class);
|
||||||
|
|
||||||
@ -116,6 +116,12 @@ public class PeerCoordinator implements PeerListener
|
|||||||
*/
|
*/
|
||||||
private final List<Piece> wantedPieces;
|
private final List<Piece> wantedPieces;
|
||||||
|
|
||||||
|
/** The total number of bytes in wantedPieces, or -1 if not yet known.
|
||||||
|
* Sync on wantedPieces.
|
||||||
|
* @since 0.9.1
|
||||||
|
*/
|
||||||
|
private long wantedBytes;
|
||||||
|
|
||||||
/** partial pieces - lock by synching on wantedPieces - TODO store Requests, not PartialPieces */
|
/** partial pieces - lock by synching on wantedPieces - TODO store Requests, not PartialPieces */
|
||||||
private final List<PartialPiece> partialPieces;
|
private final List<PartialPiece> partialPieces;
|
||||||
|
|
||||||
@ -171,16 +177,22 @@ public class PeerCoordinator implements PeerListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// only called externally from Storage after the double-check fails
|
/**
|
||||||
|
* Only called externally from Storage after the double-check fails.
|
||||||
|
* Sets wantedBytes too.
|
||||||
|
*/
|
||||||
public void setWantedPieces()
|
public void setWantedPieces()
|
||||||
{
|
{
|
||||||
if (metainfo == null || storage == null)
|
if (metainfo == null || storage == null) {
|
||||||
|
wantedBytes = -1;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
// Make a list of pieces
|
// Make a list of pieces
|
||||||
synchronized(wantedPieces) {
|
synchronized(wantedPieces) {
|
||||||
wantedPieces.clear();
|
wantedPieces.clear();
|
||||||
BitField bitfield = storage.getBitField();
|
BitField bitfield = storage.getBitField();
|
||||||
int[] pri = storage.getPiecePriorities();
|
int[] pri = storage.getPiecePriorities();
|
||||||
|
long count = 0;
|
||||||
for (int i = 0; i < metainfo.getPieces(); i++) {
|
for (int i = 0; i < metainfo.getPieces(); i++) {
|
||||||
// only add if we don't have and the priority is >= 0
|
// only add if we don't have and the priority is >= 0
|
||||||
if ((!bitfield.get(i)) &&
|
if ((!bitfield.get(i)) &&
|
||||||
@ -189,8 +201,10 @@ public class PeerCoordinator implements PeerListener
|
|||||||
if (pri != null)
|
if (pri != null)
|
||||||
p.setPriority(pri[i]);
|
p.setPriority(pri[i]);
|
||||||
wantedPieces.add(p);
|
wantedPieces.add(p);
|
||||||
|
count += metainfo.getPieceLength(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wantedBytes = count;
|
||||||
Collections.shuffle(wantedPieces, _random);
|
Collections.shuffle(wantedPieces, _random);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,7 +247,9 @@ public class PeerCoordinator implements PeerListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns how many bytes are still needed to get the complete file.
|
* Bytes not yet in storage. Does NOT account for skipped files.
|
||||||
|
* Not exact (does not adjust for last piece size).
|
||||||
|
* Returns how many bytes are still needed to get the complete torrent.
|
||||||
* @return -1 if in magnet mode
|
* @return -1 if in magnet mode
|
||||||
*/
|
*/
|
||||||
public long getLeft()
|
public long getLeft()
|
||||||
@ -244,6 +260,15 @@ public class PeerCoordinator implements PeerListener
|
|||||||
return ((long) storage.needed()) * metainfo.getPieceLength(0);
|
return ((long) storage.needed()) * metainfo.getPieceLength(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bytes still wanted. DOES account for skipped files.
|
||||||
|
* @return exact value. or -1 if no storage yet.
|
||||||
|
* @since 0.9.1
|
||||||
|
*/
|
||||||
|
public long getNeededLength() {
|
||||||
|
return wantedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of uploaded bytes of all peers.
|
* Returns the total number of uploaded bytes of all peers.
|
||||||
*/
|
*/
|
||||||
@ -330,10 +355,24 @@ public class PeerCoordinator implements PeerListener
|
|||||||
return infohash;
|
return infohash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inbound.
|
||||||
|
* Not halted, peers < max.
|
||||||
|
* @since 0.9.1
|
||||||
|
*/
|
||||||
public boolean needPeers()
|
public boolean needPeers()
|
||||||
{
|
{
|
||||||
return !halted && peers.size() < getMaxConnections();
|
return !halted && peers.size() < getMaxConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outbound.
|
||||||
|
* Not halted, peers < max, and need pieces.
|
||||||
|
* @since 0.9.1
|
||||||
|
*/
|
||||||
|
public boolean needOutboundPeers() {
|
||||||
|
return wantedBytes != 0 && needPeers();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduce max if huge pieces to keep from ooming when leeching
|
* Reduce max if huge pieces to keep from ooming when leeching
|
||||||
@ -472,7 +511,10 @@ public class PeerCoordinator implements PeerListener
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if actual attempt to add peer occurs
|
/**
|
||||||
|
* Add peer (inbound or outbound)
|
||||||
|
* @return true if actual attempt to add peer occurs
|
||||||
|
*/
|
||||||
public boolean addPeer(final Peer peer)
|
public boolean addPeer(final Peer peer)
|
||||||
{
|
{
|
||||||
if (halted)
|
if (halted)
|
||||||
@ -755,6 +797,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
if (!want.get(i)) {
|
if (!want.get(i)) {
|
||||||
Piece piece = new Piece(i);
|
Piece piece = new Piece(i);
|
||||||
wantedPieces.add(piece);
|
wantedPieces.add(piece);
|
||||||
|
wantedBytes += metainfo.getPieceLength(i);
|
||||||
// As connections are already up, new Pieces will
|
// As connections are already up, new Pieces will
|
||||||
// not have their PeerID list populated, so do that.
|
// not have their PeerID list populated, so do that.
|
||||||
for (Peer p : peers) {
|
for (Peer p : peers) {
|
||||||
@ -777,6 +820,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
} else {
|
} else {
|
||||||
iter.remove();
|
iter.remove();
|
||||||
toCancel.add(p);
|
toCancel.add(p);
|
||||||
|
wantedBytes -= metainfo.getPieceLength(p.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
@ -910,11 +954,14 @@ public class PeerCoordinator implements PeerListener
|
|||||||
throw new RuntimeException(msg, ioe);
|
throw new RuntimeException(msg, ioe);
|
||||||
}
|
}
|
||||||
wantedPieces.remove(p);
|
wantedPieces.remove(p);
|
||||||
|
wantedBytes -= metainfo.getPieceLength(p.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// just in case
|
// just in case
|
||||||
removePartialPiece(piece);
|
removePartialPiece(piece);
|
||||||
|
|
||||||
|
boolean done = wantedBytes <= 0;
|
||||||
|
|
||||||
// Announce to the world we have it!
|
// Announce to the world we have it!
|
||||||
// Disconnect from other seeders when we get the last piece
|
// Disconnect from other seeders when we get the last piece
|
||||||
List<Peer> toDisconnect = new ArrayList();
|
List<Peer> toDisconnect = new ArrayList();
|
||||||
@ -924,7 +971,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
Peer p = it.next();
|
Peer p = it.next();
|
||||||
if (p.isConnected())
|
if (p.isConnected())
|
||||||
{
|
{
|
||||||
if (completed() && p.isCompleted())
|
if (done && p.isCompleted())
|
||||||
toDisconnect.add(p);
|
toDisconnect.add(p);
|
||||||
else
|
else
|
||||||
p.have(piece);
|
p.have(piece);
|
||||||
@ -937,7 +984,11 @@ public class PeerCoordinator implements PeerListener
|
|||||||
p.disconnect(true);
|
p.disconnect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (completed()) {
|
if (done) {
|
||||||
|
// put msg on the console if partial, since Storage won't do it
|
||||||
|
if (!completed())
|
||||||
|
snark.storageCompleted(storage);
|
||||||
|
|
||||||
synchronized (partialPieces) {
|
synchronized (partialPieces) {
|
||||||
for (PartialPiece ppp : partialPieces) {
|
for (PartialPiece ppp : partialPieces) {
|
||||||
ppp.release();
|
ppp.release();
|
||||||
@ -1262,11 +1313,12 @@ public class PeerCoordinator implements PeerListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get peers from PEX -
|
||||||
* PeerListener callback
|
* PeerListener callback
|
||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
*/
|
*/
|
||||||
public void gotPeers(Peer peer, List<PeerID> peers) {
|
public void gotPeers(Peer peer, List<PeerID> peers) {
|
||||||
if (completed() || !needPeers())
|
if (!needOutboundPeers())
|
||||||
return;
|
return;
|
||||||
Destination myDest = _util.getMyDestination();
|
Destination myDest = _util.getMyDestination();
|
||||||
if (myDest == null)
|
if (myDest == null)
|
||||||
|
@ -782,6 +782,7 @@ public class Snark
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Bytes not yet in storage. Does NOT account for skipped files.
|
||||||
* @return exact value. or -1 if no storage yet.
|
* @return exact value. or -1 if no storage yet.
|
||||||
* getNeeded() * pieceLength(0) isn't accurate if last piece
|
* getNeeded() * pieceLength(0) isn't accurate if last piece
|
||||||
* is still needed.
|
* is still needed.
|
||||||
@ -802,6 +803,20 @@ public class Snark
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Bytes still wanted. DOES account for skipped files.
|
||||||
|
* FIXME -1 when not running.
|
||||||
|
* @return exact value. or -1 if no storage yet or when not running.
|
||||||
|
* @since 0.9.1
|
||||||
|
*/
|
||||||
|
public long getNeededLength() {
|
||||||
|
PeerCoordinator coord = coordinator;
|
||||||
|
if (coord != null)
|
||||||
|
return coord.getNeededLength();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does not account for skipped files.
|
||||||
* @return number of pieces still needed (magnet mode or not), or -1 if unknown
|
* @return number of pieces still needed (magnet mode or not), or -1 if unknown
|
||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
*/
|
*/
|
||||||
|
@ -338,7 +338,8 @@ public class Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Must call setPiecePriorities() after calling this
|
* Must call Snark.updatePiecePriorities()
|
||||||
|
* (which calls getPiecePriorities()) after calling this.
|
||||||
* @param file canonical path (non-directory)
|
* @param file canonical path (non-directory)
|
||||||
* @param pri default 0; <0 to disable
|
* @param pri default 0; <0 to disable
|
||||||
* @since 0.8.1
|
* @since 0.8.1
|
||||||
|
@ -23,7 +23,7 @@ package org.klomp.snark;
|
|||||||
/**
|
/**
|
||||||
* Callback used when Storage changes.
|
* Callback used when Storage changes.
|
||||||
*/
|
*/
|
||||||
public interface StorageListener
|
interface StorageListener
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Called when the storage creates a new file of a given length.
|
* Called when the storage creates a new file of a given length.
|
||||||
|
@ -72,7 +72,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
private boolean stop;
|
private boolean stop;
|
||||||
private boolean started;
|
private boolean started;
|
||||||
|
|
||||||
private List trackers;
|
private List<Tracker> trackers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param meta null if in magnet mode
|
* @param meta null if in magnet mode
|
||||||
@ -260,7 +260,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
|
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
|
||||||
Tracker tr = (Tracker)iter.next();
|
Tracker tr = (Tracker)iter.next();
|
||||||
if ((!stop) && (!tr.stop) &&
|
if ((!stop) && (!tr.stop) &&
|
||||||
(completed || coordinator.needPeers()) &&
|
(completed || coordinator.needOutboundPeers()) &&
|
||||||
(event.equals(COMPLETED_EVENT) || System.currentTimeMillis() > tr.lastRequestTime + tr.interval))
|
(event.equals(COMPLETED_EVENT) || System.currentTimeMillis() > tr.lastRequestTime + tr.interval))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -292,7 +292,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (left != 0) && (!completed) ) {
|
if (coordinator.needOutboundPeers()) {
|
||||||
// we only want to talk to new people if we need things
|
// we only want to talk to new people if we need things
|
||||||
// from them (duh)
|
// from them (duh)
|
||||||
List<Peer> ordered = new ArrayList(peers);
|
List<Peer> ordered = new ArrayList(peers);
|
||||||
@ -341,7 +341,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
} // *** end of trackers loop here
|
} // *** end of trackers loop here
|
||||||
|
|
||||||
// Get peers from PEX
|
// Get peers from PEX
|
||||||
if (left > 0 && coordinator.needPeers() && (meta == null || !meta.isPrivate()) && !stop) {
|
if (coordinator.needOutboundPeers() && (meta == null || !meta.isPrivate()) && !stop) {
|
||||||
Set<PeerID> pids = coordinator.getPEXPeers();
|
Set<PeerID> pids = coordinator.getPEXPeers();
|
||||||
if (!pids.isEmpty()) {
|
if (!pids.isEmpty()) {
|
||||||
_util.debug("Got " + pids.size() + " from PEX", Snark.INFO);
|
_util.debug("Got " + pids.size() + " from PEX", Snark.INFO);
|
||||||
@ -365,7 +365,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
// FIXME this needs to be in its own thread
|
// FIXME this needs to be in its own thread
|
||||||
if (_util.getDHT() != null && (meta == null || !meta.isPrivate()) && !stop) {
|
if (_util.getDHT() != null && (meta == null || !meta.isPrivate()) && !stop) {
|
||||||
int numwant;
|
int numwant;
|
||||||
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
if (event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
|
||||||
numwant = 1;
|
numwant = 1;
|
||||||
else
|
else
|
||||||
numwant = _util.getMaxConnections();
|
numwant = _util.getMaxConnections();
|
||||||
@ -459,7 +459,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
if (! event.equals(NO_EVENT))
|
if (! event.equals(NO_EVENT))
|
||||||
buf.append("&event=").append(event);
|
buf.append("&event=").append(event);
|
||||||
buf.append("&numwant=");
|
buf.append("&numwant=");
|
||||||
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
|
||||||
buf.append('0');
|
buf.append('0');
|
||||||
else
|
else
|
||||||
buf.append(_util.getMaxConnections());
|
buf.append(_util.getMaxConnections());
|
||||||
|
@ -890,14 +890,19 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
long total = snark.getTotalLength();
|
long total = snark.getTotalLength();
|
||||||
|
// includes skipped files, -1 for magnet mode
|
||||||
long remaining = snark.getRemainingLength();
|
long remaining = snark.getRemainingLength();
|
||||||
if (remaining > total)
|
if (remaining > total)
|
||||||
remaining = total;
|
remaining = total;
|
||||||
|
// does not include skipped files, -1 for magnet mode or when not running.
|
||||||
|
long needed = snark.getNeededLength();
|
||||||
|
if (needed > total)
|
||||||
|
needed = total;
|
||||||
long downBps = snark.getDownloadRate();
|
long downBps = snark.getDownloadRate();
|
||||||
long upBps = snark.getUploadRate();
|
long upBps = snark.getUploadRate();
|
||||||
long remainingSeconds;
|
long remainingSeconds;
|
||||||
if (downBps > 0)
|
if (downBps > 0 && needed > 0)
|
||||||
remainingSeconds = remaining / downBps;
|
remainingSeconds = needed / downBps;
|
||||||
else
|
else
|
||||||
remainingSeconds = -1;
|
remainingSeconds = -1;
|
||||||
boolean isRunning = !snark.isStopped();
|
boolean isRunning = !snark.isStopped();
|
||||||
@ -938,18 +943,31 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
|
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
|
||||||
"<br>" + err;
|
"<br>" + err;
|
||||||
}
|
}
|
||||||
} else if (remaining == 0) { // < 0 means no meta size yet
|
} else if (remaining == 0 || needed == 0) { // < 0 means no meta size yet
|
||||||
if (isRunning && curPeers > 0 && !showPeers)
|
// partial complete or seeding
|
||||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "seeding.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Seeding") +
|
if (isRunning) {
|
||||||
|
String img;
|
||||||
|
String txt;
|
||||||
|
if (remaining == 0) {
|
||||||
|
img = "seeding";
|
||||||
|
txt = _("Seeding");
|
||||||
|
} else {
|
||||||
|
// partial
|
||||||
|
img = "complete";
|
||||||
|
txt = _("Complete");
|
||||||
|
}
|
||||||
|
if (curPeers > 0 && !showPeers)
|
||||||
|
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + img + ".png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + txt +
|
||||||
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + "\">" +
|
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + "\">" +
|
||||||
curPeers + thinsp(noThinsp) +
|
curPeers + thinsp(noThinsp) +
|
||||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||||
else if (isRunning)
|
else
|
||||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "seeding.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Seeding") +
|
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + img + ".png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + txt +
|
||||||
": " + curPeers + thinsp(noThinsp) +
|
": " + curPeers + thinsp(noThinsp) +
|
||||||
ngettext("1 peer", "{0} peers", knownPeers);
|
ngettext("1 peer", "{0} peers", knownPeers);
|
||||||
else
|
} else {
|
||||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "complete.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Complete");
|
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "complete.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Complete");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
||||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "downloading.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("OK") +
|
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "downloading.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("OK") +
|
||||||
@ -1062,7 +1080,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
out.write(formatSize(uploaded));
|
out.write(formatSize(uploaded));
|
||||||
out.write("</td>\n\t");
|
out.write("</td>\n\t");
|
||||||
out.write("<td align=\"right\" class=\"snarkTorrentRateDown\">");
|
out.write("<td align=\"right\" class=\"snarkTorrentRateDown\">");
|
||||||
if(isRunning && remaining != 0)
|
if(isRunning && needed > 0)
|
||||||
out.write(formatSize(downBps) + "ps");
|
out.write(formatSize(downBps) + "ps");
|
||||||
out.write("</td>\n\t");
|
out.write("</td>\n\t");
|
||||||
out.write("<td align=\"right\" class=\"snarkTorrentRateUp\">");
|
out.write("<td align=\"right\" class=\"snarkTorrentRateUp\">");
|
||||||
@ -1108,7 +1126,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
out.write("\" onclick=\"if (!confirm('");
|
out.write("\" onclick=\"if (!confirm('");
|
||||||
// Can't figure out how to escape double quotes inside the onclick string.
|
// Can't figure out how to escape double quotes inside the onclick string.
|
||||||
// Single quotes in translate strings with parameters must be doubled.
|
// Single quotes in translate strings with parameters must be doubled.
|
||||||
// Then the remaining single quite must be escaped
|
// Then the remaining single quote must be escaped
|
||||||
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
|
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
|
||||||
out.write("')) { return false; }\"");
|
out.write("')) { return false; }\"");
|
||||||
out.write(" src=\"" + _imgPath + "remove.png\" alt=\"");
|
out.write(" src=\"" + _imgPath + "remove.png\" alt=\"");
|
||||||
@ -1127,7 +1145,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
out.write("\" onclick=\"if (!confirm('");
|
out.write("\" onclick=\"if (!confirm('");
|
||||||
// Can't figure out how to escape double quotes inside the onclick string.
|
// Can't figure out how to escape double quotes inside the onclick string.
|
||||||
// Single quotes in translate strings with parameters must be doubled.
|
// Single quotes in translate strings with parameters must be doubled.
|
||||||
// Then the remaining single quite must be escaped
|
// Then the remaining single quote must be escaped
|
||||||
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
|
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
|
||||||
out.write("')) { return false; }\"");
|
out.write("')) { return false; }\"");
|
||||||
out.write(" src=\"" + _imgPath + "delete.png\" alt=\"");
|
out.write(" src=\"" + _imgPath + "delete.png\" alt=\"");
|
||||||
@ -1194,7 +1212,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
out.write("<td class=\"snarkTorrentStatus " + rowClass + "\">");
|
out.write("<td class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||||
out.write("</td>\n\t");
|
out.write("</td>\n\t");
|
||||||
out.write("<td align=\"right\" class=\"snarkTorrentStatus " + rowClass + "\">");
|
out.write("<td align=\"right\" class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||||
if (remaining > 0) {
|
if (needed > 0) {
|
||||||
if (peer.isInteresting() && !peer.isChoked()) {
|
if (peer.isInteresting() && !peer.isChoked()) {
|
||||||
out.write("<span class=\"unchoked\">");
|
out.write("<span class=\"unchoked\">");
|
||||||
out.write(formatSize(peer.getDownloadRate()) + "ps</span>");
|
out.write(formatSize(peer.getDownloadRate()) + "ps</span>");
|
||||||
@ -1886,6 +1904,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
|||||||
else
|
else
|
||||||
buf.append("<br>").append(_("Complete"));
|
buf.append("<br>").append(_("Complete"));
|
||||||
// else unknown
|
// else unknown
|
||||||
|
long needed = snark.getNeededLength();
|
||||||
|
if (needed > 0)
|
||||||
|
buf.append("<br>").append(_("Remaining")).append(": ").append(formatSize(needed));
|
||||||
buf.append("<br>").append(_("Size")).append(": ").append(formatSize(snark.getTotalLength()));
|
buf.append("<br>").append(_("Size")).append(": ").append(formatSize(snark.getTotalLength()));
|
||||||
MetaInfo meta = snark.getMetaInfo();
|
MetaInfo meta = snark.getMetaInfo();
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
|
Reference in New Issue
Block a user