use of org.klomp.snark.Peer in project i2p.i2p by i2p.
the class I2PSnarkServlet method displaySnark.
/**
* Display one snark (one line in table, unless showPeers is true)
*
* @param stats in/out param (totals)
* @param statsOnly if true, output nothing, update stats only
* @param canWrite is the i2psnark data directory writable?
*/
private void displaySnark(PrintWriter out, HttpServletRequest req, Snark snark, String uri, int row, long[] stats, boolean showPeers, boolean isDegraded, boolean noThinsp, boolean showDebug, boolean statsOnly, boolean showRatios, boolean canWrite) throws IOException {
// stats
long uploaded = snark.getUploaded();
stats[0] += snark.getDownloaded();
stats[1] += uploaded;
long downBps = snark.getDownloadRate();
long upBps = snark.getUploadRate();
boolean isRunning = !snark.isStopped();
if (isRunning) {
stats[2] += downBps;
stats[3] += upBps;
}
int curPeers = snark.getPeerCount();
stats[4] += curPeers;
long total = snark.getTotalLength();
if (total > 0)
stats[5] += total;
if (statsOnly)
return;
String basename = snark.getBaseName();
String fullBasename = basename;
if (basename.length() > MAX_DISPLAYED_FILENAME_LENGTH) {
String start = ServletUtil.truncate(basename, MAX_DISPLAYED_FILENAME_LENGTH);
if (start.indexOf(' ') < 0 && start.indexOf('-') < 0) {
// browser has nowhere to break it
basename = start + HELLIP;
}
}
// includes skipped files, -1 for magnet mode
long remaining = snark.getRemainingLength();
if (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 remainingSeconds;
if (downBps > 0 && needed > 0)
remainingSeconds = needed / downBps;
else
remainingSeconds = -1;
MetaInfo meta = snark.getMetaInfo();
String b64 = Base64.encode(snark.getInfoHash());
String b64Short = b64.substring(0, 6);
// isValid means isNotMagnet
boolean isValid = meta != null;
boolean isMultiFile = isValid && meta.getFiles() != null;
String err = snark.getTrackerProblems();
int knownPeers = Math.max(curPeers, snark.getTrackerSeenPeers());
String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
String statusString;
if (snark.isChecking()) {
statusString = toThemeImg("stalled", "", _t("Checking")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Checking") + "</b>" + ' ' + (new DecimalFormat("0.00%")).format(snark.getCheckingProgress());
} else if (snark.isAllocating()) {
statusString = toThemeImg("stalled", "", _t("Allocating")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Allocating") + "</b>";
} else if (err != null && isRunning && curPeers == 0) {
// } else if (err != null && curPeers == 0) {
// Also don't show if seeding... but then we won't see the not-registered error
// && remaining != 0 && needed != 0) {
// let's only show this if we have no peers, otherwise PEX and DHT should bail us out, user doesn't care
// if (isRunning && curPeers > 0 && !showPeers)
// statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" +
// "<td class=\"snarkTorrentStatus " + rowClass + "\">" + _t("Tracker Error") +
// ": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + "\">" +
// curPeers + thinsp(noThinsp) +
// ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
// else if (isRunning)
// if (isRunning) {
statusString = toThemeImg("trackererror", "", err) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Tracker Error") + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
// } else {
// if (err.length() > MAX_DISPLAYED_ERROR_LENGTH)
// err = DataHelper.escapeHTML(err.substring(0, MAX_DISPLAYED_ERROR_LENGTH)) + "…";
// else
// err = DataHelper.escapeHTML(err);
// statusString = toThemeImg("trackererror", "", err) + "</td>" +
// "<td class=\"snarkTorrentStatus\">" + _t("Tracker Error");
// }
} else if (snark.isStarting()) {
statusString = toThemeImg("stalled", "", _t("Starting")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("Starting") + "</b>";
} else if (remaining == 0 || needed == 0) {
// partial complete or seeding
if (isRunning) {
String img;
String txt;
String tooltip;
if (remaining == 0) {
img = "seeding";
txt = _t("Seeding");
tooltip = _t("Seeding to {0} of {1} peers in swarm", curPeers, knownPeers);
} else {
// partial
img = "complete";
txt = _t("Complete");
tooltip = txt;
if (curPeers > 0) {
tooltip = txt + " (" + _t("Seeding to {0} of {1} peers in swarm", curPeers, knownPeers) + ")";
}
}
if (curPeers > 0 && !showPeers) {
statusString = toThemeImg(img, "", tooltip) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + txt + ":</b> <a href=\"" + uri + getQueryString(req, b64, null, null) + '#' + b64Short + "\">" + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
} else {
statusString = toThemeImg(img, "", tooltip) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + txt + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
}
} else {
statusString = toThemeImg("complete", "", _t("Complete")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("Complete") + "</b>";
}
} else {
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers) {
statusString = toThemeImg("downloading", "", _t("OK") + " (" + _t("Downloading from {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("OK") + ":</b> <a href=\"" + uri + getQueryString(req, b64, null, null) + '#' + b64Short + "\">" + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
} else if (isRunning && curPeers > 0 && downBps > 0) {
statusString = toThemeImg("downloading", "", _t("OK") + " (" + _t("Downloading from {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("OK") + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
} else if (isRunning && curPeers > 0 && !showPeers) {
statusString = toThemeImg("stalled", "", _t("Stalled") + " (" + _t("Connected to {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Stalled") + ":</b> <a href=\"" + uri + getQueryString(req, b64, null, null) + '#' + b64Short + "\">" + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
} else if (isRunning && curPeers > 0) {
statusString = toThemeImg("stalled", "", _t("Stalled") + " (" + _t("Connected to {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Stalled") + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
} else if (isRunning && knownPeers > 0) {
statusString = toThemeImg("nopeers", "", _t("No Peers") + " (" + _t("Connected to {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("No Peers") + ":</b> 0" + thinsp(noThinsp) + knownPeers;
} else if (isRunning) {
statusString = toThemeImg("nopeers", "", _t("No Peers")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("No Peers") + "</b>";
} else {
statusString = toThemeImg("stopped", "", _t("Stopped")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("Stopped") + "</b>";
}
}
out.write("<tr class=\"" + rowClass + "\" id=\"" + b64Short + "\">");
out.write("<td class=\"snarkGraphicStatus\" align=\"center\">");
out.write(statusString + "</td>\n\t");
// (i) icon column
out.write("<td class=\"snarkTrackerDetails\">");
if (isValid) {
String announce = meta.getAnnounce();
if (announce == null)
announce = snark.getTrackerURL();
if (announce != null) {
// Link to tracker details page
String trackerLink = getTrackerLink(announce, snark.getInfoHash());
if (trackerLink != null)
out.write(trackerLink);
}
}
String encodedBaseName = encodePath(fullBasename);
// File type icon column
out.write("</td>\n<td class=\"snarkTorrentDetails\">");
if (isValid) {
// Link to local details page - note that trailing slash on a single-file torrent
// gets us to the details page instead of the file.
StringBuilder buf = new StringBuilder(128);
buf.append("<a href=\"").append(encodedBaseName).append("/\" title=\"").append(_t("Torrent details")).append("\">");
out.write(buf.toString());
}
String icon;
if (isMultiFile)
icon = "folder";
else if (isValid)
icon = toIcon(meta.getName());
else if (snark instanceof FetchAndAdd)
icon = "basket_put";
else
icon = "magnet";
if (isValid) {
out.write(toImg(icon));
out.write("</a>");
} else {
out.write(toImg(icon));
}
// Torrent name column
out.write("</td><td class=\"snarkTorrentName\">");
// }
if (remaining == 0 || isMultiFile) {
StringBuilder buf = new StringBuilder(128);
buf.append("<a href=\"").append(encodedBaseName);
if (isMultiFile)
buf.append('/');
buf.append("\" title=\"");
if (isMultiFile)
buf.append(_t("View files"));
else
buf.append(_t("Open file"));
buf.append("\">");
out.write(buf.toString());
}
out.write(DataHelper.escapeHTML(basename));
if (remaining == 0 || isMultiFile)
out.write("</a>");
out.write("<td align=\"right\" class=\"snarkTorrentETA\">");
if (isRunning && remainingSeconds > 0 && !snark.isChecking())
// (eta 6h)
out.write(DataHelper.formatDuration2(Math.max(remainingSeconds, 10) * 1000));
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded\">");
if (remaining > 0) {
long percent = 100 * (total - remaining) / total;
out.write("<div class=\"percentBarOuter\">");
out.write("<div class=\"percentBarInner\" style=\"width: " + percent + "%;\">");
out.write("<div class=\"percentBarText\" tabindex=\"0\" title=\"");
out.write(percent + "% " + _t("complete") + "; " + formatSize(remaining) + ' ' + _t("remaining"));
out.write("\">");
out.write(formatSize(total - remaining) + thinsp(noThinsp) + formatSize(total));
out.write("</div></div></div>");
} else if (remaining == 0) {
// needs locale configured for automatic translation
SimpleDateFormat fmt = new SimpleDateFormat("HH:mm, EEE dd MMM yyyy");
fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
long[] dates = _manager.getSavedAddedAndCompleted(snark);
String date = fmt.format(new Date(dates[1]));
out.write("<div class=\"percentBarComplete\" title=\"");
out.write(_t("Completed") + ": " + date + "\">");
// 3GB
out.write(formatSize(total));
out.write("</div>");
}
// else
// out.write("??"); // no meta size yet
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentUploaded\">");
if (isValid) {
if (showRatios) {
if (total > 0) {
double ratio = uploaded / ((double) total);
out.write((new DecimalFormat("0.000")).format(ratio));
out.write(" x");
}
} else if (uploaded > 0) {
out.write(formatSize(uploaded));
}
}
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentRateDown\">");
if (isRunning && needed > 0)
out.write(formatSizeDec(downBps) + "ps");
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentRateUp\">");
if (isRunning && isValid)
out.write(formatSizeDec(upBps) + "ps");
out.write("</td>\n\t");
out.write("<td align=\"center\" class=\"snarkTorrentAction\">");
if (snark.isChecking()) {
// show no buttons
} else if (isRunning) {
// Stop Button
if (isDegraded)
out.write("<a href=\"" + _contextPath + "/?action=Stop_" + b64 + "&nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
else
out.write("<input type=\"image\" name=\"action_Stop_" + b64 + "\" value=\"foo\" title=\"");
out.write(_t("Stop the torrent"));
out.write("\" src=\"" + _imgPath + "stop.png\" alt=\"");
out.write(_t("Stop"));
out.write("\">");
if (isDegraded)
out.write("</a>");
} else if (!snark.isStarting()) {
if (!_manager.isStopping()) {
// This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
if (noThinsp)
out.write("<a href=\"" + _contextPath + "/?action=Start_" + b64 + "&nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
else
out.write("<input type=\"image\" name=\"action_Start_" + b64 + "\" value=\"foo\" title=\"");
out.write(_t("Start the torrent"));
out.write("\" src=\"" + _imgPath + "start.png\" alt=\"");
out.write(_t("Start"));
out.write("\">");
if (isDegraded)
out.write("</a>");
}
if (isValid && canWrite) {
// Doesnt work with Opera so use noThinsp instead of isDegraded
if (noThinsp)
out.write("<a href=\"" + _contextPath + "/?action=Remove_" + b64 + "&nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
else
out.write("<input type=\"image\" name=\"action_Remove_" + b64 + "\" value=\"foo\" title=\"");
out.write(_t("Remove the torrent from the active list, deleting the .torrent file"));
out.write("\" onclick=\"if (!confirm('");
// Can't figure out how to escape double quotes inside the onclick string.
// Single quotes in translate strings with parameters must be doubled.
// Then the remaining single quote must be escaped
out.write(_t("Are you sure you want to delete the file \\''{0}\\'' (downloaded data will not be deleted) ?", escapeJSString(snark.getName())));
out.write("')) { return false; }\"");
out.write(" src=\"" + _imgPath + "remove.png\" alt=\"");
out.write(_t("Remove"));
out.write("\">");
if (isDegraded)
out.write("</a>");
}
// We can delete magnets without write privs
if (!isValid || canWrite) {
// Doesnt work with Opera so use noThinsp instead of isDegraded
if (noThinsp)
out.write("<a href=\"" + _contextPath + "/?action=Delete_" + b64 + "&nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
else
out.write("<input type=\"image\" name=\"action_Delete_" + b64 + "\" value=\"foo\" title=\"");
out.write(_t("Delete the .torrent file and the associated data file(s)"));
out.write("\" onclick=\"if (!confirm('");
// Can't figure out how to escape double quotes inside the onclick string.
// Single quotes in translate strings with parameters must be doubled.
// Then the remaining single quote must be escaped
out.write(_t("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", escapeJSString(fullBasename)));
out.write("')) { return false; }\"");
out.write(" src=\"" + _imgPath + "delete.png\" alt=\"");
out.write(_t("Delete"));
out.write("\">");
if (isDegraded)
out.write("</a>");
}
}
out.write("</td>\n</tr>\n");
if (showPeers && isRunning && curPeers > 0) {
List<Peer> peers = snark.getPeerList();
if (!showDebug)
Collections.sort(peers, new PeerComparator());
for (Peer peer : peers) {
if (!peer.isConnected())
continue;
out.write("<tr class=\"peerinfo " + rowClass + "\"><td class=\"snarkGraphicStatus\" title=\"");
out.write(_t("Peer attached to swarm"));
out.write("\"></td><td colspan=\"4\">");
PeerID pid = peer.getPeerID();
String ch = pid != null ? pid.toString().substring(0, 4) : "????";
String client;
if ("AwMD".equals(ch))
client = _t("I2PSnark");
else if ("LUFa".equals(ch))
client = "Vuze" + getAzVersion(pid.getID());
else if ("LUJJ".equals(ch))
client = "BiglyBT" + getAzVersion(pid.getID());
else if ("LVhE".equals(ch))
client = "XD" + getAzVersion(pid.getID());
else if ("ZV".equals(ch.substring(2, 4)) || "VUZP".equals(ch))
client = "Robert" + getRobtVersion(pid.getID());
else if (// LVCS 1.0.2?; LVRS 1.0.4
ch.startsWith("LV"))
client = "Transmission" + getAzVersion(pid.getID());
else if ("LUtU".equals(ch))
client = "KTorrent" + getAzVersion(pid.getID());
else if ("CwsL".equals(ch))
client = "I2PSnarkXL";
else if ("BFJT".equals(ch))
client = "I2PRufus";
else if ("TTMt".equals(ch))
client = "I2P-BT";
else
client = _t("Unknown") + " (" + ch + ')';
out.write(client + " <tt title=\"");
out.write(_t("Destination (identity) of peer"));
out.write("\">" + peer.toString().substring(5, 9) + "</tt>");
if (showDebug)
out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s");
out.write("</td>\n\t");
out.write("<td class=\"snarkTorrentETA\">");
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded\">");
float pct;
if (isValid) {
pct = (float) (100.0 * peer.completed() / meta.getPieces());
if (pct >= 100.0)
out.write(_t("Seed"));
else {
String ps = String.valueOf(pct);
if (ps.length() > 5)
ps = ps.substring(0, 5);
out.write("<div class=\"percentBarOuter\">");
out.write("<div class=\"percentBarInner\" style=\"width:" + ps + "%;\">");
out.write("<div class=\"percentBarText\" tabindex=\"0\">" + ps + "%</div>");
out.write("</div></div>");
}
} else {
pct = (float) 101.0;
// until we get the metainfo we don't know how many pieces there are
// out.write("??");
}
out.write("</td>\n\t");
out.write("<td class=\"snarkTorrentUploaded\">");
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentRateDown\">");
if (needed > 0) {
if (peer.isInteresting() && !peer.isChoked()) {
out.write("<span class=\"unchoked\">");
out.write(formatSizeDec(peer.getDownloadRate()) + "ps</span>");
} else {
out.write("<span class=\"choked\" title=\"");
if (!peer.isInteresting())
out.write(_t("Uninteresting (The peer has no pieces we need)"));
else
out.write(_t("Choked (The peer is not allowing us to request pieces)"));
out.write("\">");
out.write(formatSizeDec(peer.getDownloadRate()) + "ps</span>");
}
} else if (!isValid) {
// if (peer supports metadata extension) {
out.write("<span class=\"unchoked\">");
out.write(formatSizeDec(peer.getDownloadRate()) + "ps</span>");
// } else {
// }
}
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentRateUp\">");
if (isValid && pct < 100.0) {
if (peer.isInterested() && !peer.isChoking()) {
out.write("<span class=\"unchoked\">");
out.write(formatSizeDec(peer.getUploadRate()) + "ps</span>");
} else {
out.write("<span class=\"choked\" title=\"");
if (!peer.isInterested())
out.write(_t("Uninterested (We have no pieces the peer needs)"));
else
out.write(_t("Choking (We are not allowing the peer to request pieces)"));
out.write("\">");
out.write(formatSizeDec(peer.getUploadRate()) + "ps</span>");
}
}
out.write("</td>\n\t");
out.write("<td class=\"snarkTorrentAction\">");
out.write("</td></tr>\n\t");
if (showDebug)
out.write("<tr class=\"debuginfo " + rowClass + "\"><td class=\"snarkGraphicStatus\"></td><td colspan=\"10\">" + peer.getSocket() + "</td></tr>");
}
}
}
Aggregations