v10.1.11: Fix mensajes de error - eliminar HTML crudo que mostraba símbolos extraños
- StreamUrlResolver ya no muestra HTML crudo en mensajes de error - Agregada validación para detectar respuestas comprimidas/binarias - Mejorados mensajes de error para ser más claros y sin caracteres raros - Agregados más patrones de extracción de URLs - Eliminado Accept-Encoding para evitar respuestas comprimidas
This commit is contained in:
@@ -14,16 +14,15 @@ import okhttp3.Response;
|
|||||||
public final class StreamUrlResolver {
|
public final class StreamUrlResolver {
|
||||||
|
|
||||||
// Múltiples patrones para extraer la URL del stream
|
// Múltiples patrones para extraer la URL del stream
|
||||||
// El proveedor cambia frecuentemente el formato
|
|
||||||
private static final Pattern[] URL_PATTERNS = {
|
private static final Pattern[] URL_PATTERNS = {
|
||||||
// Patrón original
|
|
||||||
Pattern.compile("var\\s+playbackURL\\s*=\\s*[\"']([^\"']+)[\"']"),
|
Pattern.compile("var\\s+playbackURL\\s*=\\s*[\"']([^\"']+)[\"']"),
|
||||||
// Alternativas comunes
|
|
||||||
Pattern.compile("source\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"),
|
Pattern.compile("source\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"),
|
||||||
Pattern.compile("src\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"),
|
Pattern.compile("src\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"),
|
||||||
Pattern.compile("file\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"),
|
Pattern.compile("file\\s*:\\s*[\"']([^\"']+\\.m3u8[^\"']*)[\"']"),
|
||||||
Pattern.compile("[\"'](https?://[^\"']+\\.m3u8[^\"']*)[\"']"),
|
Pattern.compile("[\"'](https?://[^\"']+\\.m3u8[^\"']*)[\"']"),
|
||||||
Pattern.compile("url\\s*:\\s*[\"']([^\"']+)[\"']"),
|
Pattern.compile("url\\s*:\\s*[\"']([^\"']+)[\"']"),
|
||||||
|
Pattern.compile("player\\s+src=\"([^\"]+)\""),
|
||||||
|
Pattern.compile("data-url=\"([^\"]+)\""),
|
||||||
};
|
};
|
||||||
|
|
||||||
private StreamUrlResolver() {
|
private StreamUrlResolver() {
|
||||||
@@ -32,60 +31,84 @@ public final class StreamUrlResolver {
|
|||||||
public static String resolve(String pageUrl) throws IOException {
|
public static String resolve(String pageUrl) throws IOException {
|
||||||
String html = downloadPage(pageUrl);
|
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
|
// Intentar todos los patrones
|
||||||
for (Pattern pattern : URL_PATTERNS) {
|
for (Pattern pattern : URL_PATTERNS) {
|
||||||
Matcher matcher = pattern.matcher(html);
|
Matcher matcher = pattern.matcher(html);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
String url = matcher.group(1);
|
String url = matcher.group(1);
|
||||||
if (url != null && !url.isEmpty() && url.startsWith("http")) {
|
if (url != null && !url.isEmpty() && url.startsWith("http")) {
|
||||||
// Limpiar la URL si tiene caracteres de escape
|
|
||||||
url = url.replace("\\", "").replace("&", "&");
|
url = url.replace("\\", "").replace("&", "&");
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si no encontramos la URL, verificar si hay un mensaje de geobloqueo o similar
|
// Verificar mensajes de error comunes en el HTML
|
||||||
if (html.contains("Access Denied") || html.contains("403 Forbidden")) {
|
String htmlLower = html.toLowerCase();
|
||||||
throw new IOException("Acceso bloqueado por el servidor. Posible geobloqueo o restricción de IP.");
|
|
||||||
|
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")) {
|
if (html.contains("404") || html.contains("Not Found") ||
|
||||||
throw new IOException("Canal no encontrado (404). El canal puede estar temporalmente fuera de servicio.");
|
htmlLower.contains("no encontrado")) {
|
||||||
|
throw new IOException("Canal no disponible (404). El canal puede estar temporalmente fuera de servicio.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mostrar un fragmento del HTML para debug
|
if (htmlLower.contains("maintenance") || htmlLower.contains("mantenimiento")) {
|
||||||
String preview = html.length() > 800 ? html.substring(0, 800) : html;
|
throw new IOException("El servidor está en mantenimiento. Intenta más tarde.");
|
||||||
throw new IOException("No se encontró la URL del stream. Vista previa: " + preview);
|
}
|
||||||
|
|
||||||
|
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String downloadPage(String pageUrl) throws IOException {
|
private static String downloadPage(String pageUrl) throws IOException {
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(pageUrl)
|
.url(pageUrl)
|
||||||
.header("User-Agent", NetworkUtils.getUserAgent())
|
.header("User-Agent", NetworkUtils.getUserAgent())
|
||||||
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
|
.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("Accept-Language", "es-ES,es;q=0.9,en;q=0.8")
|
||||||
.header("Accept-Encoding", "gzip, deflate, br")
|
|
||||||
.header("Referer", "https://streamtp10.com/")
|
.header("Referer", "https://streamtp10.com/")
|
||||||
.header("Connection", "keep-alive")
|
.header("Connection", "keep-alive")
|
||||||
.header("Upgrade-Insecure-Requests", "1")
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = NetworkUtils.getClient().newCall(request).execute()) {
|
try (Response response = NetworkUtils.getClient().newCall(request).execute()) {
|
||||||
if (!response.isSuccessful()) {
|
if (!response.isSuccessful()) {
|
||||||
if (response.code() == 403) {
|
if (response.code() == 403) {
|
||||||
throw new IOException("Acceso denegado (403). El servidor puede estar bloqueando tu conexión.");
|
throw new IOException("Acceso denegado (403). El servidor está bloqueando la conexión.");
|
||||||
} else if (response.code() == 404) {
|
} else if (response.code() == 404) {
|
||||||
throw new IOException("Canal no encontrado (404).");
|
throw new IOException("Canal no encontrado (404).");
|
||||||
|
} else if (response.code() == 401) {
|
||||||
|
throw new IOException("Acceso no autorizado (401).");
|
||||||
} else {
|
} else {
|
||||||
throw new IOException("Error HTTP " + response.code() + " al cargar la página del stream");
|
throw new IOException("Error HTTP " + response.code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (response.body() == null) {
|
if (response.body() == null) {
|
||||||
throw new IOException("Respuesta vacía del servidor");
|
throw new IOException("Respuesta vacía del servidor");
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.body().string();
|
String body = response.body().string();
|
||||||
|
|
||||||
|
// Limitar tamaño para evitar problemas de memoria
|
||||||
|
if (body.length() > 500000) {
|
||||||
|
body = body.substring(0, 500000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user