🎉 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
TELEGRAM_BOT_TOKEN=8593525164:AAGCX9B_RJGN35_F7tSB72rEZhS_4Zpcszs
TELEGRAM_CHAT_ID=692714536

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.streamplayer"
minSdk 21
targetSdk 35
versionCode 100101
versionName "10.1.1"
versionCode 100102
versionName "10.1.2"
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 boolean overlayVisible = true;
private OkHttpClient okHttpClient;
private int retryCount = 0;
private static final int MAX_RETRIES = 3;
private String lastStreamUrl;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -102,6 +105,7 @@ public class PlayerActivity extends AppCompatActivity {
private void loadChannel() {
showLoading(true);
retryCount = 0; // Resetear contador al cargar nuevo canal
new Thread(() -> {
try {
String resolvedUrl = StreamUrlResolver.resolve(channelUrl);
@@ -117,14 +121,17 @@ public class PlayerActivity extends AppCompatActivity {
private void startPlayback(String streamUrl) {
try {
releasePlayer();
lastStreamUrl = streamUrl; // Guardar URL para reintentos
retryCount = 0; // Resetear contador al iniciar nueva reproducción
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this)
.setEnableDecoderFallback(true)
.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);
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();
trackSelector.setParameters(params);
@@ -140,6 +147,7 @@ public class PlayerActivity extends AppCompatActivity {
public void onPlaybackStateChanged(int playbackState) {
if (playbackState == Player.STATE_READY) {
showLoading(false);
retryCount = 0; // Resetear contador de reintentos al reproducir exitosamente
} else if (playbackState == Player.STATE_BUFFERING) {
showLoading(true);
}
@@ -147,9 +155,46 @@ public class PlayerActivity extends AppCompatActivity {
@Override
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() : "";
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 {
OkHttpClient bootstrap = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
@@ -228,8 +273,8 @@ public class PlayerActivity extends AppCompatActivity {
.build();
} catch (UnknownHostException e) {
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
}