diff --git a/src/main/java/cz/kamma/xtreamplayer/XtreamLibraryService.java b/src/main/java/cz/kamma/xtreamplayer/XtreamLibraryService.java index e572d98..2350c6f 100644 --- a/src/main/java/cz/kamma/xtreamplayer/XtreamLibraryService.java +++ b/src/main/java/cz/kamma/xtreamplayer/XtreamLibraryService.java @@ -23,6 +23,9 @@ import java.util.Set; final class XtreamLibraryService { private static final Logger LOGGER = LogManager.getLogger(XtreamLibraryService.class); private static final Set SENSITIVE_KEYS = Set.of("password", "pass", "pwd", "token", "authorization"); + private static final String DEFAULT_BROWSER_UA = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"; static final List LOAD_STEPS = List.of( "live_categories", "live_streams", @@ -417,7 +420,7 @@ final class XtreamLibraryService { HttpRequest request = HttpRequest.newBuilder(candidate) .GET() .timeout(Duration.ofSeconds(30)) - .header("User-Agent", "XtreamPlayer/1.0") + .header("User-Agent", DEFAULT_BROWSER_UA) .header("Accept", "application/json,text/plain,*/*") .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); diff --git a/src/main/java/cz/kamma/xtreamplayer/XtreamPlayerApplication.java b/src/main/java/cz/kamma/xtreamplayer/XtreamPlayerApplication.java index 2e92581..06fe24a 100644 --- a/src/main/java/cz/kamma/xtreamplayer/XtreamPlayerApplication.java +++ b/src/main/java/cz/kamma/xtreamplayer/XtreamPlayerApplication.java @@ -38,6 +38,9 @@ public final class XtreamPlayerApplication { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final Logger LOGGER = LogManager.getLogger(XtreamPlayerApplication.class); private static final Pattern URI_ATTR_PATTERN = Pattern.compile("URI=\"([^\"]+)\""); + private static final String DEFAULT_BROWSER_UA = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"; private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(20)) .followRedirects(HttpClient.Redirect.NORMAL) @@ -237,12 +240,22 @@ public final class XtreamPlayerApplication { } try { - HttpRequest request = HttpRequest.newBuilder(target) + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(target) .GET() .timeout(Duration.ofSeconds(60)) - .header("User-Agent", "XtreamPlayer/1.0") - .header("Accept", "*/*") - .build(); + .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, "Referer"); + copyRequestHeaderIfPresent(exchange, requestBuilder, "Origin"); + HttpRequest request = requestBuilder.build(); HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofByteArray()); String contentType = response.headers().firstValue("Content-Type").orElse("application/octet-stream"); byte[] body = response.body() == null ? new byte[0] : response.body(); @@ -255,6 +268,10 @@ public final class XtreamPlayerApplication { return; } + 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); writeBytes(exchange, response.statusCode(), body, contentType); } catch (InterruptedException interruptedException) { @@ -618,7 +635,7 @@ public final class XtreamPlayerApplication { HttpRequest request = HttpRequest.newBuilder(uri) .GET() .timeout(Duration.ofSeconds(25)) - .header("User-Agent", "XtreamPlayer/1.0") + .header("User-Agent", DEFAULT_BROWSER_UA) .header("Accept", "application/json,text/plain,*/*") .build(); return HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofByteArray()); @@ -749,6 +766,27 @@ public final class XtreamPlayerApplication { return "/api/stream-proxy?url=" + urlEncode(absoluteUrl); } + private static void copyRequestHeaderIfPresent(HttpExchange exchange, HttpRequest.Builder requestBuilder, String headerName) { + String value = exchange.getRequestHeaders().getFirst(headerName); + if (value != null && !value.isBlank()) { + requestBuilder.header(headerName, value); + } + } + + private static void copyResponseHeaderIfPresent(HttpResponse response, HttpExchange exchange, String headerName) { + String value = response.headers().firstValue(headerName).orElse(""); + if (!value.isBlank()) { + exchange.getResponseHeaders().set(headerName, value); + } + } + + private static String firstNonBlank(String primary, String fallback) { + if (primary != null && !primary.isBlank()) { + return primary; + } + return fallback; + } + private static Map parseKeyValue(String raw) { Map result = new LinkedHashMap<>(); if (raw == null || raw.isBlank()) {