mkv chunking
This commit is contained in:
parent
4ae128f35d
commit
b8df5d0997
@ -244,6 +244,11 @@ public final class XtreamPlayerApplication {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLikelyLargeVodFile(target, sourceUrl)) {
|
||||
proxyLargeMediaStream(exchange, target, sourceUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
List<URI> attempts = candidateUris(target);
|
||||
HttpResponse<byte[]> response = null;
|
||||
@ -798,10 +803,103 @@ public final class XtreamPlayerApplication {
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static boolean isLikelyLargeVodFile(URI uri, String sourceUrl) {
|
||||
if (sourceUrl != null && !sourceUrl.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
String path = uri == null || uri.getPath() == null ? "" : uri.getPath().toLowerCase(Locale.ROOT);
|
||||
return path.endsWith(".mkv") || path.contains(".mkv?")
|
||||
|| path.endsWith(".mp4") || path.contains(".mp4?");
|
||||
}
|
||||
|
||||
private static void proxyLargeMediaStream(HttpExchange exchange, URI target, String sourceUrl) throws IOException {
|
||||
List<URI> attempts = candidateUris(target);
|
||||
List<String> attemptErrors = new ArrayList<>();
|
||||
for (URI candidate : attempts) {
|
||||
try {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(candidate)
|
||||
.GET()
|
||||
.header("User-Agent", firstNonBlank(
|
||||
exchange.getRequestHeaders().getFirst("User-Agent"),
|
||||
DEFAULT_BROWSER_UA
|
||||
))
|
||||
.header("Accept", firstNonBlank(
|
||||
exchange.getRequestHeaders().getFirst("Accept"),
|
||||
"*/*"
|
||||
));
|
||||
copyRequestHeaderIfPresent(exchange, requestBuilder, "Range");
|
||||
copyRequestHeaderIfPresent(exchange, requestBuilder, "If-Range");
|
||||
copyRequestHeaderIfPresent(exchange, requestBuilder, "Accept-Encoding");
|
||||
copyRequestHeaderIfPresent(exchange, requestBuilder, "Cache-Control");
|
||||
copyRequestHeaderIfPresent(exchange, requestBuilder, "Pragma");
|
||||
String referer = resolveRefererForCandidate(exchange, candidate, sourceUrl);
|
||||
if (!referer.isBlank()) {
|
||||
requestBuilder.header("Referer", referer);
|
||||
}
|
||||
HttpResponse<InputStream> response = HTTP_CLIENT.send(
|
||||
requestBuilder.build(),
|
||||
HttpResponse.BodyHandlers.ofInputStream()
|
||||
);
|
||||
int status = response.statusCode();
|
||||
String contentType = response.headers().firstValue("Content-Type").orElse("application/octet-stream");
|
||||
if (status >= 400) {
|
||||
attemptErrors.add(maskUri(candidate) + " -> HTTP " + status);
|
||||
try (InputStream ignored = response.body()) {
|
||||
// close upstream body
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
copyResponseHeaderIfPresent(response, exchange, "Accept-Ranges");
|
||||
copyResponseHeaderIfPresent(response, exchange, "Content-Range");
|
||||
copyResponseHeaderIfPresent(response, exchange, "Cache-Control");
|
||||
copyResponseHeaderIfPresent(response, exchange, "Expires");
|
||||
exchange.getResponseHeaders().set("Content-Type", contentType);
|
||||
response.headers().firstValue("Content-Length")
|
||||
.ifPresent(value -> exchange.getResponseHeaders().set("Content-Length", value));
|
||||
|
||||
long responseLength = parseContentLength(response.headers().firstValue("Content-Length").orElse(""));
|
||||
exchange.sendResponseHeaders(status, responseLength >= 0 ? responseLength : 0);
|
||||
long sent = 0;
|
||||
try (InputStream inputStream = response.body()) {
|
||||
byte[] buffer = new byte[64 * 1024];
|
||||
int read;
|
||||
while ((read = inputStream.read(buffer)) >= 0) {
|
||||
exchange.getResponseBody().write(buffer, 0, read);
|
||||
sent += read;
|
||||
}
|
||||
} finally {
|
||||
logApiResponse(exchange, status, (int) Math.min(Integer.MAX_VALUE, sent), contentType);
|
||||
exchange.close();
|
||||
}
|
||||
return;
|
||||
} catch (InterruptedException interruptedException) {
|
||||
Thread.currentThread().interrupt();
|
||||
writeJson(exchange, 500, errorJson("Stream proxy interrupted."));
|
||||
return;
|
||||
} catch (Exception exception) {
|
||||
attemptErrors.add(maskUri(candidate) + " -> " + compactError(exception));
|
||||
LOGGER.warn("Large media proxy candidate failed uri={} reason={}", maskUri(candidate), compactError(exception));
|
||||
}
|
||||
}
|
||||
writeJson(exchange, 502, errorJson("Unable to proxy large media stream. Attempts: " + String.join(" | ", attemptErrors)));
|
||||
}
|
||||
|
||||
private static String proxyStreamUrl(String absoluteUrl, String sourceUrl) {
|
||||
return "/api/stream-proxy?url=" + urlEncode(absoluteUrl) + "&src=" + urlEncode(sourceUrl);
|
||||
}
|
||||
|
||||
private static long parseContentLength(String value) {
|
||||
if (value == null || value.isBlank()) {
|
||||
return -1L;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(value.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
||||
private static UpstreamResult retrySegmentUsingFreshPlaylist(HttpExchange exchange, URI originalSegmentUri, String sourceUrl)
|
||||
throws IOException, InterruptedException {
|
||||
URI sourceUri = URI.create(sourceUrl);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user