diff --git a/app/src/main/java/com/fongmi/android/tv/server/process/Local.java b/app/src/main/java/com/fongmi/android/tv/server/process/Local.java index a7ded6e43..3ac89b612 100644 --- a/app/src/main/java/com/fongmi/android/tv/server/process/Local.java +++ b/app/src/main/java/com/fongmi/android/tv/server/process/Local.java @@ -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 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 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; } }