package com.streamplayer; import android.content.Context; import android.content.SharedPreferences; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import okhttp3.Request; import okhttp3.Response; public class EventRepository { private static final String PREFS_NAME = "events_cache"; private static final String KEY_JSON = "json"; private static final String KEY_TIMESTAMP = "timestamp"; private static final long CACHE_DURATION = 24L * 60 * 60 * 1000; // 24 horas // URL única para eventos (actualizado para evitar bloqueos de ISP) private static final String EVENTS_URL = "http://streamtp10.com/eventos.json"; public interface Callback { void onSuccess(List events); void onError(String message); } public void loadEvents(Context context, boolean forceRefresh, Callback callback) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); long last = prefs.getLong(KEY_TIMESTAMP, 0); long now = System.currentTimeMillis(); if (!forceRefresh && now - last < CACHE_DURATION) { String cachedJson = prefs.getString(KEY_JSON, null); if (cachedJson != null) { try { callback.onSuccess(parseEvents(cachedJson)); return; } catch (JSONException ignored) { } } } new Thread(() -> { try { String json = downloadJson(context); List events = parseEvents(json); prefs.edit().putString(KEY_JSON, json).putLong(KEY_TIMESTAMP, System.currentTimeMillis()).apply(); callback.onSuccess(events); } catch (IOException | JSONException e) { String cachedJson = prefs.getString(KEY_JSON, null); if (cachedJson != null) { try { callback.onSuccess(parseEvents(cachedJson)); return; } catch (JSONException ignored) { } } callback.onError(e.getMessage() != null ? e.getMessage() : "Error desconocido"); } }).start(); } private String downloadJson(Context context) throws IOException { Request request = new Request.Builder() .url(EVENTS_URL) .header("User-Agent", NetworkUtils.getUserAgent()) .header("Accept", "application/json") .build(); try (Response response = NetworkUtils.getClient().newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Error HTTP " + response.code() + ": " + response.message()); } if (response.body() == null) { throw new IOException("Respuesta vacía del servidor"); } String contentType = response.header("Content-Type"); // Permitir json o text/plain (Raw de Gitea a veces es text/plain) if (contentType != null && !contentType.contains("json") && !contentType.contains("text/plain")) { // Aceptamos text/plain también por flexibilidad } String responseBody = response.body().string(); // Validar que no sea HTML if (responseBody.trim().startsWith(" parseEvents(String json) throws JSONException { if (json == null || json.trim().isEmpty()) { throw new JSONException("La respuesta está vacía"); } // Validar que no sea HTML antes de parsear String trimmed = json.trim(); if (trimmed.startsWith(" events = new ArrayList<>(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); for (int i = 0; i < array.length(); i++) { JSONObject obj = array.getJSONObject(i); String title = obj.optString("title"); String time = obj.optString("time"); String category = obj.optString("category"); String status = obj.optString("status"); String link = obj.optString("link"); String normalized = normalizeLink(link); // Ajustar hora: la web muestra hora de España, Argentina es +2 horas String displayTime = time; try { if (time != null && !time.isEmpty()) { LocalTime localTime = LocalTime.parse(time.trim(), formatter); LocalTime adjustedTime = localTime.plusHours(2); displayTime = adjustedTime.format(formatter); } } catch (DateTimeParseException ignored) { } long startMillis = parseEventTime(time); events.add(new EventItem(title, displayTime, category, status, normalized, extractChannelName(link), startMillis)); } return Collections.unmodifiableList(events); } private String normalizeLink(String link) { if (link == null) { return ""; } // Actualizado a streamtp10.com String updated = link.replace("streamtpmedia.com", "streamtp10.com") .replace("streamtpcloud.com", "streamtp10.com"); return updated.replace("global1.php", "global2.php"); } private String extractChannelName(String link) { if (link == null) { return ""; } int idx = link.indexOf("stream="); if (idx == -1) { return ""; } return link.substring(idx + 7).replace("_", " ").toUpperCase(); } private long parseEventTime(String time) { if (time == null || time.isEmpty()) { return -1; } try { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); LocalTime localTime = LocalTime.parse(time.trim(), formatter); // Ajustar hora: la web muestra hora de España, Argentina es +2 horas LocalTime adjustedTime = localTime.plusHours(2); ZoneId zone = ZoneId.of("America/Argentina/Buenos_Aires"); LocalDate today = LocalDate.now(zone); ZonedDateTime start = ZonedDateTime.of(LocalDateTime.of(today, adjustedTime), zone); ZonedDateTime now = ZonedDateTime.now(zone); if (start.isBefore(now.minusHours(12))) { start = start.plusDays(1); } return start.toInstant().toEpochMilli(); } catch (DateTimeParseException e) { return -1; } } }