From cff9658060ab38a165a6f678749074b2671a272c Mon Sep 17 00:00:00 2001 From: Renato Date: Sun, 15 Feb 2026 19:18:01 -0300 Subject: [PATCH] =?UTF-8?q?v10.1.12:=20Revertir=20a=20c=C3=B3digo=20v10.1.?= =?UTF-8?q?7=20funcional=20con=20dominio=20streamtp10.com?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PlayerActivity: Referer vuelve a ser channelUrl (URL específica del canal) - StreamUrlResolver: Vuelve a crear su propio cliente con Google DNS - Eliminados cambios problemáticos de SSL trust-all y múltiples DNS - Mantenidos solo los cambios necesarios: streamtp10.com en lugar de streamtpcloud.com Esto debería hacer que los streams vuelvan a funcionar como en v10.1.7 --- .../java/com/streamplayer/PlayerActivity.java | 77 +++++++---- .../com/streamplayer/StreamUrlResolver.java | 122 ++++++++---------- 2 files changed, 106 insertions(+), 93 deletions(-) diff --git a/app/src/main/java/com/streamplayer/PlayerActivity.java b/app/src/main/java/com/streamplayer/PlayerActivity.java index 2edec8a..6f1a5cd 100644 --- a/app/src/main/java/com/streamplayer/PlayerActivity.java +++ b/app/src/main/java/com/streamplayer/PlayerActivity.java @@ -25,10 +25,16 @@ import androidx.media3.common.util.Util; import androidx.annotation.OptIn; import androidx.media3.common.util.UnstableApi; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; +import okhttp3.HttpUrl; import okhttp3.OkHttpClient; +import okhttp3.dnsoverhttps.DnsOverHttps; @OptIn(markerClass = UnstableApi.class) public class PlayerActivity extends AppCompatActivity { @@ -48,6 +54,7 @@ public class PlayerActivity extends AppCompatActivity { private String channelName; private String channelUrl; private boolean overlayVisible = true; + private OkHttpClient okHttpClient; private int retryCount = 0; private static final int MAX_RETRIES = 3; private String lastStreamUrl; @@ -80,7 +87,7 @@ public class PlayerActivity extends AppCompatActivity { initViews(); channelLabel.setText(channelName); - // DNS over HTTPS ya está configurado en NetworkUtils + // DNS configurado en StreamUrlResolver loadChannel(); } @@ -103,8 +110,10 @@ public class PlayerActivity extends AppCompatActivity { try { String resolvedUrl = StreamUrlResolver.resolve(channelUrl); runOnUiThread(() -> startPlayback(resolvedUrl)); - } catch (Exception e) { + } catch (IOException e) { runOnUiThread(() -> showError("No se pudo conectar con el canal: " + e.getMessage())); + } catch (Exception e) { + runOnUiThread(() -> showError("Error inesperado: " + e.getMessage())); } }).start(); } @@ -155,17 +164,13 @@ public class PlayerActivity extends AppCompatActivity { boolean isRetryableError = fullError.contains("404") || fullError.contains("403") || - fullError.contains("401") || fullError.contains("timeout") || fullError.contains("Unable to connect") || fullError.contains("Network") || fullError.contains("source error") || - fullError.contains("SSL") || - fullError.contains("Certificate") || error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED || error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT || - error.errorCode == PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS || - error.errorCode == PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE; + error.errorCode == PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS; if (isRetryableError && retryCount < MAX_RETRIES) { retryCount++; @@ -174,12 +179,14 @@ public class PlayerActivity extends AppCompatActivity { showError("Error de conexión. Reintentando... (" + retryCount + "/" + MAX_RETRIES + ")"); }); - // Reintentar después de 3 segundos + // Reintentar después de 2 segundos new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(() -> { if (lastStreamUrl != null) { - loadChannel(); // Recargar desde la página para obtener URL fresca + startPlayback(lastStreamUrl); + } else { + loadChannel(); } - }, 3000); + }, 2000); } else { // Mostrar error final después de agotar reintentos String finalMessage = "Error al reproducir: " + fullError; @@ -228,27 +235,53 @@ public class PlayerActivity extends AppCompatActivity { private MediaSource buildMediaSource(MediaItem mediaItem) { Map headers = new HashMap<>(); - - // Headers críticos para que el servidor acepte la petición - headers.put("Referer", "https://streamtp10.com/"); + headers.put("Referer", channelUrl); headers.put("Origin", "https://streamtp10.com"); headers.put("Accept", "*/*"); - headers.put("Accept-Language", "es-ES,es;q=0.9"); - headers.put("Accept-Encoding", "gzip, deflate, br"); - - // Usar el User-Agent estándar de NetworkUtils que funciona mejor - String userAgent = NetworkUtils.getUserAgent(); + headers.put("Connection", "keep-alive"); - // Usar NetworkUtils.getClient() que tiene los 4 servidores DNS configurados - OkHttpClient client = NetworkUtils.getClient(); + String userAgent = Util.getUserAgent(this, "StreamPlayer"); - OkHttpDataSource.Factory factory = new OkHttpDataSource.Factory(client) + OkHttpDataSource.Factory factory = new OkHttpDataSource.Factory(provideOkHttpClient()) .setUserAgent(userAgent) .setDefaultRequestProperties(headers); - return new HlsMediaSource.Factory(factory).createMediaSource(mediaItem); } + private OkHttpClient provideOkHttpClient() { + if (okHttpClient != null) { + return okHttpClient; + } + + try { + OkHttpClient bootstrap = new OkHttpClient.Builder() + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .retryOnConnectionFailure(true) + .build(); + + DnsOverHttps dohDns = 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(); + + okHttpClient = bootstrap.newBuilder() + .dns(dohDns) + .build(); + } catch (UnknownHostException e) { + okHttpClient = new OkHttpClient.Builder() + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .retryOnConnectionFailure(true) + .build(); + } + + return okHttpClient; + } + @Override protected void onStart() { super.onStart(); diff --git a/app/src/main/java/com/streamplayer/StreamUrlResolver.java b/app/src/main/java/com/streamplayer/StreamUrlResolver.java index e42f9f4..7e3e8a9 100644 --- a/app/src/main/java/com/streamplayer/StreamUrlResolver.java +++ b/app/src/main/java/com/streamplayer/StreamUrlResolver.java @@ -1,114 +1,94 @@ package com.streamplayer; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +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 extrayendo playbackURL de la página. - * Utiliza NetworkUtils para configuración centralizada de DNS. + * Utiliza DNS de Google para evitar bloqueos. */ public final class StreamUrlResolver { - // Múltiples patrones para extraer la URL del stream - private static final Pattern[] URL_PATTERNS = { - Pattern.compile("var\\s+playbackURL\\s*=\\s*[\"']([^\"']+)[\"']"), - Pattern.compile("source\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"), - Pattern.compile("src\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"), - Pattern.compile("file\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"), - Pattern.compile("[\"'](https?://[^\"']+\\.m3u8[^\"']*)[\"']"), - Pattern.compile("url\\s*:\\s*[\"']([^\"']+)[\"']"), - Pattern.compile("player\\s+src=\"([^\"]+)\""), - Pattern.compile("data-url=\"([^\"]+)\""), - }; + // Patrón para extraer la URL del stream directamente + private static final Pattern PLAYBACK_URL_PATTERN = + Pattern.compile("var\\s+playbackURL\\s*=\\s*[\"']([^\"']+)[\"']"); + private static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"; + + 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 + } + + CLIENT = builder.build(); + } + private StreamUrlResolver() { } public static String resolve(String pageUrl) throws IOException { String html = downloadPage(pageUrl); - // Verificar si es HTML válido o recibimos basura/compresión - if (!html.contains("<") && !html.contains(">")) { - // Probablemente respuesta comprimida o binaria - throw new IOException("El servidor devolvió una respuesta no válida. Posible bloqueo del ISP."); - } - - // Intentar todos los patrones - for (Pattern pattern : URL_PATTERNS) { - Matcher matcher = pattern.matcher(html); - if (matcher.find()) { - String url = matcher.group(1); - if (url != null && !url.isEmpty() && url.startsWith("http")) { - url = url.replace("\\", "").replace("&", "&"); - return url; - } + // Buscar playbackURL directamente en el HTML + Matcher matcher = PLAYBACK_URL_PATTERN.matcher(html); + if (matcher.find()) { + String url = matcher.group(1); + if (url != null && !url.isEmpty() && url.startsWith("http")) { + return url; } } - // Verificar mensajes de error comunes en el HTML - String htmlLower = html.toLowerCase(); - - if (html.contains("Access Denied") || html.contains("403 Forbidden") || - htmlLower.contains("acceso denegado") || htmlLower.contains("access denied")) { - throw new IOException("Acceso bloqueado por el servidor. El proveedor del stream puede estar restringiendo el acceso desde tu ubicación."); - } - - if (html.contains("404") || html.contains("Not Found") || - htmlLower.contains("no encontrado")) { - throw new IOException("Canal no disponible (404). El canal puede estar temporalmente fuera de servicio."); - } - - if (htmlLower.contains("maintenance") || htmlLower.contains("mantenimiento")) { - throw new IOException("El servidor está en mantenimiento. Intenta más tarde."); - } - - if (htmlLower.contains("banned") || htmlLower.contains("blocked") || - htmlLower.contains("bloqueado") || htmlLower.contains("suspendido")) { - throw new IOException("Acceso suspendido o bloqueado por el proveedor."); - } - - // Error genérico sin mostrar HTML crudo (evita símbolos extraños) - throw new IOException("No se pudo obtener el stream. El proveedor puede haber cambiado el formato o el canal está offline."); + // Si no encontramos la URL, mostrar un fragmento del HTML para debug + String preview = html.length() > 500 ? html.substring(0, 500) : html; + throw new IOException("No se encontró la URL del stream en la página. Vista previa: " + preview); } private static String downloadPage(String pageUrl) throws IOException { Request request = new Request.Builder() .url(pageUrl) - .header("User-Agent", NetworkUtils.getUserAgent()) + .header("User-Agent", USER_AGENT) .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") .header("Accept-Language", "es-ES,es;q=0.9,en;q=0.8") .header("Referer", "https://streamtp10.com/") - .header("Connection", "keep-alive") .build(); - try (Response response = NetworkUtils.getClient().newCall(request).execute()) { + try (Response response = CLIENT.newCall(request).execute()) { if (!response.isSuccessful()) { - if (response.code() == 403) { - throw new IOException("Acceso denegado (403). El servidor está bloqueando la conexión."); - } else if (response.code() == 404) { - throw new IOException("Canal no encontrado (404)."); - } else if (response.code() == 401) { - throw new IOException("Acceso no autorizado (401)."); - } else { - throw new IOException("Error HTTP " + response.code()); - } + 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 body = response.body().string(); - - // Limitar tamaño para evitar problemas de memoria - if (body.length() > 500000) { - body = body.substring(0, 500000); - } - - return body; + return response.body().string(); } } }