diff --git a/app/build.gradle b/app/build.gradle index b9a8695..bb23e1e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.streamplayer" minSdk 21 targetSdk 33 - versionCode 100200 - versionName "10.0.2" + versionCode 100300 + versionName "10.0.3" buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"' } diff --git a/app/src/main/java/com/streamplayer/PlayerActivity.java b/app/src/main/java/com/streamplayer/PlayerActivity.java index e5743a8..5c95b9c 100644 --- a/app/src/main/java/com/streamplayer/PlayerActivity.java +++ b/app/src/main/java/com/streamplayer/PlayerActivity.java @@ -203,10 +203,10 @@ public class PlayerActivity extends AppCompatActivity { DnsOverHttps dohDns = new DnsOverHttps.Builder() .client(bootstrap) - .url(HttpUrl.get("https://dns.adguard-dns.com/dns-query")) + .url(HttpUrl.get("https://dns.google/dns-query")) .bootstrapDnsHosts( - InetAddress.getByName("94.140.14.14"), - InetAddress.getByName("94.140.15.15")) + InetAddress.getByName("8.8.8.8"), + InetAddress.getByName("8.8.4.4")) .build(); okHttpClient = bootstrap.newBuilder() diff --git a/app/src/main/java/com/streamplayer/StreamUrlResolver.java b/app/src/main/java/com/streamplayer/StreamUrlResolver.java index 7c9f5e8..95dfdb0 100644 --- a/app/src/main/java/com/streamplayer/StreamUrlResolver.java +++ b/app/src/main/java/com/streamplayer/StreamUrlResolver.java @@ -1,22 +1,29 @@ package com.streamplayer; import android.util.Base64; +import android.util.Log; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.dnsoverhttps.DnsOverHttps; + /** - * Resuelve la URL real del stream analizando el JavaScript ofuscado de streamtpmedia. + * Resuelve la URL real del stream analizando el JavaScript ofuscado de streamtpcloud. + * Utiliza DNS de Google para evitar bloqueos. */ public final class StreamUrlResolver { @@ -26,6 +33,32 @@ public final class StreamUrlResolver { private static final Pattern KEY_FUNCTIONS_PATTERN = Pattern.compile("var\\s+k=(\\w+)\\(\\)\\+(\\w+)\\(\\);"); private static final String FUNCTION_TEMPLATE = "function\\s+%s\\(\\)\\s*\\{\\s*return\\s+(\\d+);\\s*\\}"; private static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 13) ExoPlayerResolver/1.0"; + + private static final OkHttpClient CLIENT; + + static { + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .followRedirects(true); + + try { + // DNS de Google (8.8.8.8) + OkHttpClient bootstrap = new OkHttpClient.Builder().build(); + DnsOverHttps dns = new DnsOverHttps.Builder() + .client(bootstrap) + .url(HttpUrl.get("https://dns.google/dns-query")) + .bootstrapDnsHosts( + InetAddress.getByName("8.8.8.8"), + InetAddress.getByName("8.8.4.4")) + .build(); + builder.dns(dns); + } catch (UnknownHostException e) { + // Fallback a DNS del sistema si falla la inicialización (raro con IPs hardcoded) + } + + CLIENT = builder.build(); + } private StreamUrlResolver() { } @@ -56,46 +89,37 @@ public final class StreamUrlResolver { } private static String downloadPage(String pageUrl) throws IOException { - HttpURLConnection connection = (HttpURLConnection) new URL(pageUrl).openConnection(); - connection.setConnectTimeout(15000); - connection.setReadTimeout(15000); - connection.setRequestProperty("User-Agent", USER_AGENT); - connection.setRequestProperty("Accept", "text/html,application/xhtml+xml"); - - try { - int responseCode = connection.getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_OK) { - throw new IOException("Error HTTP " + responseCode + " al cargar la página del stream"); + Request request = new Request.Builder() + .url(pageUrl) + .header("User-Agent", USER_AGENT) + .header("Accept", "text/html,application/xhtml+xml") + .build(); + + try (Response response = CLIENT.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException("Error HTTP " + response.code() + " al cargar la página del stream"); + } + if (response.body() == null) { + throw new IOException("Respuesta vacía del servidor"); } - String contentType = connection.getContentType(); - // Validar que sea contenido web (HTML) + // Validar Content-Type + String contentType = response.header("Content-Type"); if (contentType != null && !contentType.contains("html") && !contentType.contains("text")) { - // A veces puede venir sin content type o application/octet-stream, - // pero si es explícitamente una imagen o algo así, abortamos. - if (contentType.startsWith("image/") || contentType.startsWith("video/") || contentType.startsWith("audio/")) { + if (contentType.startsWith("image/") || contentType.startsWith("video/") || contentType.startsWith("audio/")) { throw new IOException("El servidor devolvió " + contentType + " en lugar de HTML"); } } - - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { - StringBuilder builder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - return builder.toString(); - } - } finally { - connection.disconnect(); + + return response.body().string(); } } private static long extractKeyOffset(String html) throws IOException { Matcher matcher = KEY_FUNCTIONS_PATTERN.matcher(html); if (!matcher.find()) { - throw new IOException("No se encontró la clave del stream"); + // Logging para debug si es necesario, pero lanzamos excepción limpia + throw new IOException("No se encontró la clave del stream (posible bloqueo o cambio en la página)"); } String first = matcher.group(1); String second = matcher.group(2); diff --git a/update-manifest.json b/update-manifest.json index 211588f..af868ff 100644 --- a/update-manifest.json +++ b/update-manifest.json @@ -1,10 +1,10 @@ { - "versionCode": 100200, - "versionName": "10.0.2", + "versionCode": 100300, + "versionName": "10.0.3", "minSupportedVersionCode": 91000, "forceUpdate": false, - "downloadUrl": "https://gitea.cbcren.online/renato97/app/releases/download/v10.0.2/StreamPlayer-v10.0.2.apk", - "fileName": "StreamPlayer-v10.0.2.apk", + "downloadUrl": "https://gitea.cbcren.online/renato97/app/releases/download/v10.0.3/StreamPlayer-v10.0.3.apk", + "fileName": "StreamPlayer-v10.0.3.apk", "sizeBytes": 0, - "notes": "StreamPlayer v10.0.2\n\nNovedades:\n- Eventos centralizados en servidor propio\n- Corrección de carga de eventos" + "notes": "StreamPlayer v10.0.3\n\nNovedades:\n- Fix: Evasión de bloqueos regionales mediante DNS de Google (DoH)\n- Corrección de error 'No se encontró la clave del stream'" } \ No newline at end of file