forked from I2P_Developers/i2p.i2p
i2psnark: Support "preview" HTML5 play for partial files
This commit is contained in:
@ -427,15 +427,18 @@ public class Storage implements Closeable
|
|||||||
****/
|
****/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For efficiency, calculate remaining bytes for all files at once
|
* For efficiency, calculate remaining bytes for all files at once.
|
||||||
|
* Remaining bytes is rv[0]. Preview bytes is rv[1].
|
||||||
*
|
*
|
||||||
* @return number of bytes remaining for each file, use indexOf() to get index for a file
|
* @return number of bytes remaining and number of bytes available for a preview for each file, use indexOf() to get index for a file
|
||||||
* @since 0.9.23
|
* @since 0.9.23
|
||||||
*/
|
*/
|
||||||
public long[] remaining() {
|
public long[][] remaining() {
|
||||||
long[] rv = new long[_torrentFiles.size()];
|
long[] rv = new long[_torrentFiles.size()];
|
||||||
|
long[] pv = new long[_torrentFiles.size()];
|
||||||
|
long[][] rva = new long[][] { rv, pv };
|
||||||
if (complete())
|
if (complete())
|
||||||
return rv;
|
return rva;
|
||||||
long bytes = 0;
|
long bytes = 0;
|
||||||
for (int i = 0; i < _torrentFiles.size(); i++) {
|
for (int i = 0; i < _torrentFiles.size(); i++) {
|
||||||
TorrentFile tf = _torrentFiles.get(i);
|
TorrentFile tf = _torrentFiles.get(i);
|
||||||
@ -443,10 +446,23 @@ public class Storage implements Closeable
|
|||||||
long end = start + tf.length;
|
long end = start + tf.length;
|
||||||
int pc = (int) (bytes / piece_size);
|
int pc = (int) (bytes / piece_size);
|
||||||
long rvi = 0;
|
long rvi = 0;
|
||||||
if (!bitfield.get(pc))
|
long pvi = 0;
|
||||||
rvi = Math.min(piece_size - (start % piece_size), tf.length);
|
long first = Math.min(piece_size - (start % piece_size), tf.length);
|
||||||
|
if (bitfield.get(pc))
|
||||||
|
pvi = first;
|
||||||
|
else
|
||||||
|
rvi = first;
|
||||||
|
boolean preview = true;
|
||||||
for (int j = pc + 1; (((long)j) * piece_size) < end && j < pieces; j++) {
|
for (int j = pc + 1; (((long)j) * piece_size) < end && j < pieces; j++) {
|
||||||
if (!bitfield.get(j)) {
|
if (bitfield.get(j)) {
|
||||||
|
if (preview) {
|
||||||
|
if (((long)(j+1))*piece_size < end)
|
||||||
|
pvi += piece_size;
|
||||||
|
else
|
||||||
|
pvi += end - (((long)j) * piece_size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
preview = false;
|
||||||
if (((long)(j+1))*piece_size < end)
|
if (((long)(j+1))*piece_size < end)
|
||||||
rvi += piece_size;
|
rvi += piece_size;
|
||||||
else
|
else
|
||||||
@ -454,9 +470,10 @@ public class Storage implements Closeable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
rv[i] = rvi;
|
rv[i] = rvi;
|
||||||
|
pv[i] = pvi;
|
||||||
bytes += tf.length;
|
bytes += tf.length;
|
||||||
}
|
}
|
||||||
return rv;
|
return rva;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,7 +179,7 @@ class BasicServlet extends HttpServlet
|
|||||||
* @param pathInContext The path to find a resource for.
|
* @param pathInContext The path to find a resource for.
|
||||||
* @return The resource to serve or null. Returns null for directories
|
* @return The resource to serve or null. Returns null for directories
|
||||||
*/
|
*/
|
||||||
public HttpContent getContent(String pathInContext)
|
public HttpContent getContent(String pathInContext, long limit)
|
||||||
{
|
{
|
||||||
HttpContent r = null;
|
HttpContent r = null;
|
||||||
if (_warBase != null && pathInContext.startsWith(_warBase)) {
|
if (_warBase != null && pathInContext.startsWith(_warBase)) {
|
||||||
@ -187,8 +187,12 @@ class BasicServlet extends HttpServlet
|
|||||||
} else {
|
} else {
|
||||||
File f = getResource(pathInContext);
|
File f = getResource(pathInContext);
|
||||||
// exists && !directory
|
// exists && !directory
|
||||||
if (f != null && f.isFile())
|
if (f != null && f.isFile()) {
|
||||||
r = new FileContent(f);
|
if (limit > 0)
|
||||||
|
r = new LimitFileContent(f, limit);
|
||||||
|
else
|
||||||
|
r = new FileContent(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -202,10 +206,17 @@ class BasicServlet extends HttpServlet
|
|||||||
String pathInfo=request.getPathInfo();
|
String pathInfo=request.getPathInfo();
|
||||||
// ??? right??
|
// ??? right??
|
||||||
String pathInContext = addPaths(servletpath, pathInfo);
|
String pathInContext = addPaths(servletpath, pathInfo);
|
||||||
|
long limit = 0;
|
||||||
|
String slimit = request.getParameter("limit");
|
||||||
|
if (slimit != null) {
|
||||||
|
try {
|
||||||
|
limit = Long.parseLong(slimit);
|
||||||
|
} catch (NumberFormatException nfe) {}
|
||||||
|
}
|
||||||
|
|
||||||
// Find the resource and content
|
// Find the resource and content
|
||||||
try {
|
try {
|
||||||
HttpContent content = getContent(pathInContext);
|
HttpContent content = getContent(pathInContext, limit);
|
||||||
|
|
||||||
// Handle resource
|
// Handle resource
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
@ -468,6 +479,29 @@ class BasicServlet extends HttpServlet
|
|||||||
public String toString() { return "File \"" + _file + '"'; }
|
public String toString() { return "File \"" + _file + '"'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.45
|
||||||
|
*/
|
||||||
|
private class LimitFileContent extends FileContent
|
||||||
|
{
|
||||||
|
private final long _limit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param limit must be less than file length
|
||||||
|
*/
|
||||||
|
public LimitFileContent(File file, long limit)
|
||||||
|
{
|
||||||
|
super(file);
|
||||||
|
_limit = Math.min(limit, file.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLength()
|
||||||
|
{
|
||||||
|
return _limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class JarContent implements HttpContent
|
private class JarContent implements HttpContent
|
||||||
{
|
{
|
||||||
private final String _path;
|
private final String _path;
|
||||||
|
@ -3375,9 +3375,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
|
|
||||||
List<Sorters.FileAndIndex> fileList = new ArrayList<Sorters.FileAndIndex>(ls.length);
|
List<Sorters.FileAndIndex> fileList = new ArrayList<Sorters.FileAndIndex>(ls.length);
|
||||||
// precompute remaining for all files for efficiency
|
// precompute remaining for all files for efficiency
|
||||||
long[] remainingArray = (storage != null) ? storage.remaining() : null;
|
long[][] arrays = (storage != null) ? storage.remaining() : null;
|
||||||
|
long[] remainingArray = (arrays != null) ? arrays[0] : null;
|
||||||
|
long[] previewArray = (arrays != null) ? arrays[1] : null;
|
||||||
for (int i = 0; i < ls.length; i++) {
|
for (int i = 0; i < ls.length; i++) {
|
||||||
fileList.add(new Sorters.FileAndIndex(ls[i], storage, remainingArray));
|
fileList.add(new Sorters.FileAndIndex(ls[i], storage, remainingArray, previewArray));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean showSort = fileList.size() > 1;
|
boolean showSort = fileList.size() > 1;
|
||||||
@ -3546,25 +3548,30 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
if (mime == null)
|
if (mime == null)
|
||||||
mime = "";
|
mime = "";
|
||||||
|
|
||||||
boolean isAudio = false;
|
boolean isAudio = isAudio(mime);
|
||||||
boolean isVideo = false;
|
boolean isVideo = !isAudio && isVideo(mime);
|
||||||
buf.append("<td class=\"snarkFileIcon\">");
|
buf.append("<td class=\"snarkFileIcon\">");
|
||||||
if (complete) {
|
String preview = null;
|
||||||
isAudio = isAudio(mime);
|
if (complete || (isAudio && fai.preview > 1024*100) || (isVideo && fai.preview > 1024*1024)) {
|
||||||
isVideo = !isAudio && isVideo(mime);
|
String ppath = complete ? path : path + "?limit=" + fai.preview;
|
||||||
|
if (!complete) {
|
||||||
|
double pct = fai.preview / (double) fai.length;
|
||||||
|
preview = "<br>" + _t("Preview") + ": " +
|
||||||
|
(new DecimalFormat("0.00%")).format(pct);
|
||||||
|
}
|
||||||
if (isAudio || isVideo) {
|
if (isAudio || isVideo) {
|
||||||
// HTML5
|
// HTML5
|
||||||
if (isAudio)
|
if (isAudio)
|
||||||
buf.append("<audio");
|
buf.append("<audio");
|
||||||
else
|
else
|
||||||
buf.append("<video");
|
buf.append("<video");
|
||||||
buf.append(" controls><source src=\"").append(path).append("\" type=\"").append(mime).append("\">");
|
buf.append(" controls><source src=\"").append(ppath).append("\" type=\"").append(mime).append("\">");
|
||||||
}
|
}
|
||||||
buf.append("<a href=\"").append(path).append("\">");
|
buf.append("<a href=\"").append(ppath).append("\">");
|
||||||
if (mime.startsWith("image/")) {
|
if (mime.startsWith("image/")) {
|
||||||
// thumbnail
|
// thumbnail
|
||||||
buf.append("<img alt=\"\" border=\"0\" class=\"thumb\" src=\"")
|
buf.append("<img alt=\"\" border=\"0\" class=\"thumb\" src=\"")
|
||||||
.append(path).append("\"></a>");
|
.append(ppath).append("\"></a>");
|
||||||
} else {
|
} else {
|
||||||
buf.append(toImg(icon, _t("Open"))).append("</a>");
|
buf.append(toImg(icon, _t("Open"))).append("</a>");
|
||||||
}
|
}
|
||||||
@ -3588,6 +3595,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
buf.append(DataHelper.escapeHTML(item.getName()));
|
buf.append(DataHelper.escapeHTML(item.getName()));
|
||||||
if (complete)
|
if (complete)
|
||||||
buf.append("</a>");
|
buf.append("</a>");
|
||||||
|
if (preview != null)
|
||||||
|
buf.append(preview);
|
||||||
buf.append("</td><td align=right class=\"snarkFileSize\">");
|
buf.append("</td><td align=right class=\"snarkFileSize\">");
|
||||||
if (!item.isDirectory())
|
if (!item.isDirectory())
|
||||||
buf.append(formatSize(length));
|
buf.append(formatSize(length));
|
||||||
@ -3755,7 +3764,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
return null;
|
return null;
|
||||||
List<Sorters.FileAndIndex> fileList = new ArrayList<Sorters.FileAndIndex>(ls.length);
|
List<Sorters.FileAndIndex> fileList = new ArrayList<Sorters.FileAndIndex>(ls.length);
|
||||||
// precompute remaining for all files for efficiency
|
// precompute remaining for all files for efficiency
|
||||||
long[] remainingArray = (storage != null) ? storage.remaining() : null;
|
long[] remainingArray = (storage != null) ? storage.remaining()[0] : null;
|
||||||
for (int i = 0; i < ls.length; i++) {
|
for (int i = 0; i < ls.length; i++) {
|
||||||
fileList.add(new Sorters.FileAndIndex(ls[i], storage, remainingArray));
|
fileList.add(new Sorters.FileAndIndex(ls[i], storage, remainingArray));
|
||||||
}
|
}
|
||||||
|
@ -374,6 +374,7 @@ class Sorters {
|
|||||||
public final boolean isDirectory;
|
public final boolean isDirectory;
|
||||||
public final long length;
|
public final long length;
|
||||||
public final long remaining;
|
public final long remaining;
|
||||||
|
public final long preview;
|
||||||
public final int priority;
|
public final int priority;
|
||||||
public final int index;
|
public final int index;
|
||||||
|
|
||||||
@ -382,15 +383,25 @@ class Sorters {
|
|||||||
* @param remainingArray precomputed, non-null iff storage is non-null
|
* @param remainingArray precomputed, non-null iff storage is non-null
|
||||||
*/
|
*/
|
||||||
public FileAndIndex(File file, Storage storage, long[] remainingArray) {
|
public FileAndIndex(File file, Storage storage, long[] remainingArray) {
|
||||||
|
this(file, storage, remainingArray, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param storage may be null
|
||||||
|
* @param remainingArray precomputed, non-null iff storage is non-null
|
||||||
|
*/
|
||||||
|
public FileAndIndex(File file, Storage storage, long[] remainingArray, long[] previewArray) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
index = storage != null ? storage.indexOf(file) : -1;
|
index = storage != null ? storage.indexOf(file) : -1;
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
isDirectory = false;
|
isDirectory = false;
|
||||||
remaining = remainingArray[index];
|
remaining = remainingArray[index];
|
||||||
|
preview = previewArray != null ? previewArray[index] : 0;
|
||||||
priority = storage.getPriority(index);
|
priority = storage.getPriority(index);
|
||||||
} else {
|
} else {
|
||||||
isDirectory = file.isDirectory();
|
isDirectory = file.isDirectory();
|
||||||
remaining = -1;
|
remaining = -1;
|
||||||
|
preview = 0;
|
||||||
priority = -999;
|
priority = -999;
|
||||||
}
|
}
|
||||||
length = isDirectory ? 0 : file.length();
|
length = isDirectory ? 0 : file.length();
|
||||||
|
Reference in New Issue
Block a user