🎉 v10.1.2: Reproductor robusto con calidad adaptativa

 Mejoras principales:
- Reproductor más robusto: reintenta automáticamente en errores 404
- Calidad adaptativa: eliminado forzamiento de 1080p
- Sistema de reintentos: 3 intentos automáticos con delays
- Timeouts mejorados: 20s connect, 30s read
- Manejo granular de errores recuperables

📱 Cambios técnicos:
- setForceHighestSupportedBitrate(false) - calidad adaptativa
- Configuración de reintentos (MAX_RETRIES = 3)
- Detección de errores 404, 403, timeout, network
- Feedback visual durante reintentos
- Timeouts incrementados para conexiones lentas

🔧 Build:
- Incrementado versionCode: 100101 → 100102
- Incrementado versionName: 10.1.1 → 10.1.2
- APK compilado y subido a Gitea

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
renato97
2026-01-28 21:26:58 +01:00
parent e34323c2da
commit 3c1a323b35
3 changed files with 56 additions and 11 deletions

2
.env
View File

@@ -1,4 +1,4 @@
GITEA_TOKEN=7921aa22187b39125d29399d26f527ba26a2fb5b GITEA_TOKEN=efeed2af00597883adb04da70bd6a7c2993ae92d
GEMINI_API_KEY=AIzaSyDWOgyAJqscuPU6iSpS6gxupWBm4soNw5o GEMINI_API_KEY=AIzaSyDWOgyAJqscuPU6iSpS6gxupWBm4soNw5o
TELEGRAM_BOT_TOKEN=8593525164:AAGCX9B_RJGN35_F7tSB72rEZhS_4Zpcszs TELEGRAM_BOT_TOKEN=8593525164:AAGCX9B_RJGN35_F7tSB72rEZhS_4Zpcszs
TELEGRAM_CHAT_ID=692714536 TELEGRAM_CHAT_ID=692714536

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.streamplayer" applicationId "com.streamplayer"
minSdk 21 minSdk 21
targetSdk 35 targetSdk 35
versionCode 100101 versionCode 100102
versionName "10.1.1" versionName "10.1.2"
buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"' buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"'
} }

View File

@@ -55,6 +55,9 @@ public class PlayerActivity extends AppCompatActivity {
private String channelUrl; private String channelUrl;
private boolean overlayVisible = true; private boolean overlayVisible = true;
private OkHttpClient okHttpClient; private OkHttpClient okHttpClient;
private int retryCount = 0;
private static final int MAX_RETRIES = 3;
private String lastStreamUrl;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -102,6 +105,7 @@ public class PlayerActivity extends AppCompatActivity {
private void loadChannel() { private void loadChannel() {
showLoading(true); showLoading(true);
retryCount = 0; // Resetear contador al cargar nuevo canal
new Thread(() -> { new Thread(() -> {
try { try {
String resolvedUrl = StreamUrlResolver.resolve(channelUrl); String resolvedUrl = StreamUrlResolver.resolve(channelUrl);
@@ -117,14 +121,17 @@ public class PlayerActivity extends AppCompatActivity {
private void startPlayback(String streamUrl) { private void startPlayback(String streamUrl) {
try { try {
releasePlayer(); releasePlayer();
lastStreamUrl = streamUrl; // Guardar URL para reintentos
retryCount = 0; // Resetear contador al iniciar nueva reproducción
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this) DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this)
.setEnableDecoderFallback(true) .setEnableDecoderFallback(true)
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON); .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON);
// Configurar track selector para máxima calidad // Configurar track selector para calidad adaptativa (no forzar máxima calidad)
trackSelector = new DefaultTrackSelector(this); trackSelector = new DefaultTrackSelector(this);
DefaultTrackSelector.Parameters params = trackSelector.buildUponParameters() DefaultTrackSelector.Parameters params = trackSelector.buildUponParameters()
.setForceHighestSupportedBitrate(true) // Forzar máximo bitrate .setForceHighestSupportedBitrate(false) // Permitir calidad adaptativa
.setMaxVideoBitrate(Integer.MAX_VALUE) // Sin límite máximo de bitrate
.build(); .build();
trackSelector.setParameters(params); trackSelector.setParameters(params);
@@ -140,6 +147,7 @@ public class PlayerActivity extends AppCompatActivity {
public void onPlaybackStateChanged(int playbackState) { public void onPlaybackStateChanged(int playbackState) {
if (playbackState == Player.STATE_READY) { if (playbackState == Player.STATE_READY) {
showLoading(false); showLoading(false);
retryCount = 0; // Resetear contador de reintentos al reproducir exitosamente
} else if (playbackState == Player.STATE_BUFFERING) { } else if (playbackState == Player.STATE_BUFFERING) {
showLoading(true); showLoading(true);
} }
@@ -147,9 +155,46 @@ public class PlayerActivity extends AppCompatActivity {
@Override @Override
public void onPlayerError(PlaybackException error) { public void onPlayerError(PlaybackException error) {
String detail = error.getCause() != null ? String errorMsg = error.getMessage() != null ? error.getMessage() : "";
String detail = error.getCause() != null ?
error.getCause().getMessage() : ""; error.getCause().getMessage() : "";
showError("Error al reproducir: " + error.getMessage() + " " + detail); String fullError = errorMsg + " " + detail;
// Verificar si es un error que justifica reintento (404, conectividad, etc.)
boolean isRetryableError =
fullError.contains("404") ||
fullError.contains("403") ||
fullError.contains("timeout") ||
fullError.contains("Unable to connect") ||
fullError.contains("Network") ||
fullError.contains("source error") ||
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;
if (isRetryableError && retryCount < MAX_RETRIES) {
retryCount++;
runOnUiThread(() -> {
showLoading(true);
showError("Error de conexión. Reintentando... (" + retryCount + "/" + MAX_RETRIES + ")");
});
// Reintentar después de 2 segundos
new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
if (lastStreamUrl != null) {
startPlayback(lastStreamUrl);
} else {
loadChannel();
}
}, 2000);
} else {
// Mostrar error final después de agotar reintentos
String finalMessage = "Error al reproducir: " + fullError;
if (retryCount >= MAX_RETRIES) {
finalMessage += "\n\nSe agotaron los reintentos (" + MAX_RETRIES + ").";
}
showError(finalMessage);
}
} }
}); });
@@ -210,8 +255,8 @@ public class PlayerActivity extends AppCompatActivity {
try { try {
OkHttpClient bootstrap = new OkHttpClient.Builder() OkHttpClient bootstrap = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
.build(); .build();
@@ -228,8 +273,8 @@ public class PlayerActivity extends AppCompatActivity {
.build(); .build();
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
okHttpClient = new OkHttpClient.Builder() okHttpClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
.build(); .build();
} }