|
|
|
|
@ -4,13 +4,13 @@ import com.fongmi.android.tv.server.Nano; |
|
|
|
|
import com.fongmi.android.tv.server.impl.Process; |
|
|
|
|
import com.fongmi.android.tv.utils.FileUtil; |
|
|
|
|
import com.github.catvod.utils.Path; |
|
|
|
|
import com.google.common.net.HttpHeaders; |
|
|
|
|
import com.google.gson.JsonArray; |
|
|
|
|
import com.google.gson.JsonObject; |
|
|
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
|
import java.io.FileInputStream; |
|
|
|
|
import java.io.FileNotFoundException; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.text.SimpleDateFormat; |
|
|
|
|
import java.util.Date; |
|
|
|
|
import java.util.List; |
|
|
|
|
@ -97,64 +97,66 @@ public class Local implements Process { |
|
|
|
|
return Nano.ok(info.toString()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private NanoHTTPD.Response getFile(Map<String, String> header, File file, String mime) throws Exception { |
|
|
|
|
long startFrom = 0; |
|
|
|
|
long endAt = -1; |
|
|
|
|
String range = header.get("range"); |
|
|
|
|
if (range != null) { |
|
|
|
|
if (range.startsWith("bytes=")) { |
|
|
|
|
range = range.substring("bytes=".length()); |
|
|
|
|
int minus = range.indexOf('-'); |
|
|
|
|
try { |
|
|
|
|
if (minus > 0) { |
|
|
|
|
startFrom = Long.parseLong(range.substring(0, minus)); |
|
|
|
|
endAt = Long.parseLong(range.substring(minus + 1)); |
|
|
|
|
} |
|
|
|
|
} catch (NumberFormatException ignored) { |
|
|
|
|
} |
|
|
|
|
private NanoHTTPD.Response getFile(Map<String, String> headers, File file, String mime) throws IOException { |
|
|
|
|
if (mime == null) mime = "application/octet-stream"; |
|
|
|
|
long fileLen = file.length(); |
|
|
|
|
long startFrom = 0, endAt = fileLen - 1; |
|
|
|
|
String range = headers.get("range"); |
|
|
|
|
|
|
|
|
|
if (range != null && range.startsWith("bytes=")) { |
|
|
|
|
try { |
|
|
|
|
String[] parts = range.substring(6).split("-", 2); |
|
|
|
|
if (!parts[0].isEmpty()) startFrom = Long.parseLong(parts[0]); |
|
|
|
|
if (parts.length > 1 && !parts[1].isEmpty()) endAt = Long.parseLong(parts[1]); |
|
|
|
|
if (startFrom > endAt) startFrom = 0; |
|
|
|
|
if (endAt >= fileLen) endAt = fileLen - 1; |
|
|
|
|
} catch (NumberFormatException ignored) { |
|
|
|
|
startFrom = 0; |
|
|
|
|
endAt = fileLen - 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String ifRange = headers.get("if-range"); |
|
|
|
|
String ifNoneMatch = headers.get("if-none-match"); |
|
|
|
|
String etag = Integer.toHexString((file.getAbsolutePath() + file.lastModified() + fileLen).hashCode()); |
|
|
|
|
boolean ifRangeMatch = (ifRange == null || ifRange.equals(etag)); |
|
|
|
|
boolean ifNoneMatchHit = ifNoneMatch != null && ("*".equals(ifNoneMatch) || ifNoneMatch.equals(etag)); |
|
|
|
|
|
|
|
|
|
long contentLength; |
|
|
|
|
NanoHTTPD.Response res; |
|
|
|
|
long fileLen = file.length(); |
|
|
|
|
String ifRange = header.get("if-range"); |
|
|
|
|
String etag = Integer.toHexString((file.getAbsolutePath() + file.lastModified() + "" + file.length()).hashCode()); |
|
|
|
|
boolean headerIfRangeMissingOrMatching = (ifRange == null || etag.equals(ifRange)); |
|
|
|
|
String ifNoneMatch = header.get("if-none-match"); |
|
|
|
|
boolean headerIfNoneMatchPresentAndMatching = ifNoneMatch != null && ("*".equals(ifNoneMatch) || ifNoneMatch.equals(etag)); |
|
|
|
|
if (headerIfRangeMissingOrMatching && range != null && startFrom >= 0 && startFrom < fileLen) { |
|
|
|
|
if (headerIfNoneMatchPresentAndMatching) { |
|
|
|
|
|
|
|
|
|
if (ifRangeMatch && range != null && startFrom < fileLen) { |
|
|
|
|
if (ifNoneMatchHit) { |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_MODIFIED, mime, ""); |
|
|
|
|
res.addHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); |
|
|
|
|
res.addHeader(HttpHeaders.ETAG, etag); |
|
|
|
|
contentLength = 0; |
|
|
|
|
} else { |
|
|
|
|
if (endAt < 0) endAt = fileLen - 1; |
|
|
|
|
long newLen = endAt - startFrom + 1; |
|
|
|
|
if (newLen < 0) newLen = 0; |
|
|
|
|
FileInputStream fis = new FileInputStream(file); |
|
|
|
|
fis.skip(startFrom); |
|
|
|
|
long skipped = 0; |
|
|
|
|
while (skipped < startFrom) { |
|
|
|
|
long s = fis.skip(startFrom - skipped); |
|
|
|
|
if (s <= 0) break; |
|
|
|
|
skipped += s; |
|
|
|
|
} |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.PARTIAL_CONTENT, mime, fis, newLen); |
|
|
|
|
res.addHeader(HttpHeaders.CONTENT_LENGTH, newLen + ""); |
|
|
|
|
res.addHeader(HttpHeaders.CONTENT_RANGE, "bytes " + startFrom + "-" + endAt + "/" + fileLen); |
|
|
|
|
res.addHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); |
|
|
|
|
res.addHeader(HttpHeaders.ETAG, etag); |
|
|
|
|
res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen); |
|
|
|
|
contentLength = newLen; |
|
|
|
|
} |
|
|
|
|
} else if (range != null && startFrom >= fileLen) { |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, ""); |
|
|
|
|
res.addHeader("Content-Range", "bytes */" + fileLen); |
|
|
|
|
contentLength = 0; |
|
|
|
|
} else if (ifNoneMatchHit) { |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_MODIFIED, mime, ""); |
|
|
|
|
contentLength = 0; |
|
|
|
|
} else { |
|
|
|
|
if (headerIfRangeMissingOrMatching && range != null && startFrom >= fileLen) { |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, ""); |
|
|
|
|
res.addHeader(HttpHeaders.CONTENT_RANGE, "bytes */" + fileLen); |
|
|
|
|
res.addHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); |
|
|
|
|
res.addHeader(HttpHeaders.ETAG, etag); |
|
|
|
|
} else if (headerIfNoneMatchPresentAndMatching && (!headerIfRangeMissingOrMatching || range == null)) { |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_MODIFIED, mime, ""); |
|
|
|
|
res.addHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); |
|
|
|
|
res.addHeader(HttpHeaders.ETAG, etag); |
|
|
|
|
} else { |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, mime, new FileInputStream(file), (int) file.length()); |
|
|
|
|
res.addHeader(HttpHeaders.CONTENT_LENGTH, fileLen + ""); |
|
|
|
|
res.addHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); |
|
|
|
|
res.addHeader(HttpHeaders.ETAG, etag); |
|
|
|
|
} |
|
|
|
|
FileInputStream fis = new FileInputStream(file); |
|
|
|
|
res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, mime, fis, fileLen); |
|
|
|
|
contentLength = fileLen; |
|
|
|
|
} |
|
|
|
|
res.addHeader("Content-Length", String.valueOf(contentLength)); |
|
|
|
|
res.addHeader("Accept-Ranges", "bytes"); |
|
|
|
|
res.addHeader("ETag", etag); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|