diff --git a/.env b/.env deleted file mode 100644 index 65c01bd..0000000 --- a/.env +++ /dev/null @@ -1,4 +0,0 @@ -GITEA_TOKEN=efeed2af00597883adb04da70bd6a7c2993ae92d -GEMINI_API_KEY=AIzaSyDWOgyAJqscuPU6iSpS6gxupWBm4soNw5o -TELEGRAM_BOT_TOKEN=8593525164:AAGCX9B_RJGN35_F7tSB72rEZhS_4Zpcszs -TELEGRAM_CHAT_ID=692714536 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 091dc35..0000000 --- a/Dockerfile +++ /dev/null @@ -1,57 +0,0 @@ -FROM eclipse-temurin:17-jdk - -# Evitar interactividad durante la instalación -ENV DEBIAN_FRONTEND=noninteractive - -# Instalar dependencias necesarias para Android SDK -RUN apt-get update && apt-get install -y \ - wget \ - unzip \ - git \ - python3 \ - python3-pip \ - ncurses-bin \ - build-essential \ - lib32z1 \ - lib32ncurses6 \ - lib32stdc++6 \ - zlib1g-dev \ - && rm -rf /var/lib/apt/lists/* - -# Instalar Android SDK -ENV ANDROID_SDK_ROOT=/opt/android-sdk -ENV SDKMANAGER="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" - -RUN mkdir -p $ANDROID_SDK_ROOT/cmdline-tools && \ - wget -q https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O tools.zip && \ - unzip -q tools.zip && \ - mv cmdline-tools $ANDROID_SDK_ROOT/cmdline-tools/latest && \ - rm tools.zip - -# Aceptar licencias -RUN yes | $SDKMANAGER --licenses - -# Instalar componentes necesarios -RUN $SDKMANAGER "platform-tools" "platforms;android-33" "build-tools;33.0.2" "platforms;android-31" - -# Instalar Gradle -ENV GRADLE_HOME=/opt/gradle -RUN wget -q https://services.gradle.org/distributions/gradle-8.2-bin.zip -O gradle.zip && \ - unzip -q gradle.zip && \ - mv gradle-8.2 $GRADLE_HOME && \ - rm gradle.zip - -ENV PATH=$PATH:$GRADLE_HOME/bin:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools - -# Copiar proyecto -COPY . /app -WORKDIR /app - -# Dar permisos de ejecución a gradlew -RUN chmod +x ./gradlew - -# Construir APK -RUN ./gradlew assembleRelease - -# Comando para copiar APK a un volumen montado -CMD ["cp", "/app/app/build/outputs/apk/release/app-release.apk", "/output/StreamPlayer-v10.0.apk"] \ No newline at end of file diff --git a/Dockerfile.mullvad b/Dockerfile.mullvad deleted file mode 100644 index 5127e79..0000000 --- a/Dockerfile.mullvad +++ /dev/null @@ -1,24 +0,0 @@ -FROM ubuntu:22.04 - -RUN apt-get update && apt-get install -y \ - curl \ - wireguard-tools \ - iputils-ping \ - dnsutils \ - iproute2 \ - && rm -rf /var/lib/apt/lists/* - -RUN mkdir -p /etc/wireguard - -RUN echo '[Interface]' > /etc/wireguard/wg0.conf && \ - echo 'PrivateKey = Tq9/VQ8qdsphS+0nVEFmWgFvMfvJ2FbWGK/Xt9cX4AA=' >> /etc/wireguard/wg0.conf && \ - echo 'Address = 10.8.0.2/32' >> /etc/wireguard/wg0.conf && \ - echo 'DNS = 1.1.1.1' >> /etc/wireguard/wg0.conf && \ - echo '' >> /etc/wireguard/wg0.conf && \ - echo '[Peer]' >> /etc/wireguard/wg0.conf && \ - echo 'PublicKey = 03qeK7CSn6wcMzfqilmVt6Tf81VZIPWnSG04euSkyxM=' >> /etc/wireguard/wg0.conf && \ - echo 'Endpoint = 149.88.104.2:51820' >> /etc/wireguard/wg0.conf && \ - echo 'AllowedIPs = 0.0.0.0/0' >> /etc/wireguard/wg0.conf && \ - echo 'PersistentKeepalive = 25' >> /etc/wireguard/wg0.conf - -CMD tail -f /dev/null diff --git a/README.md b/README.md index 4070b69..7571629 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,27 @@ -# 🏆 Furbo VPN Edition +# StreamPlayer VPN Edition [![Android](https://img.shields.io/badge/Platform-Android-green.svg)](https://android.com) -[![Version](https://img.shields.io/badge/Version-10.2.0-blue.svg)]() -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![License](https://badge.fba7af7372a48fda78fb377067a7233d.com/MIT-blue.svg)](LICENSE) -**StreamPlayer con VPN integrada** - La única app de streaming con Mullvad VPN built-in. +**StreamPlayer con VPN integrada** - App de streaming deportivo con Mullvad VPN built-in. -## ✨ Características +## Características -- 📺 **Streaming**: Reproducción de canales en vivo -- 🔒 **VPN Integrada**: Con Mullvad WireGuard - sin app externa -- 🌎 **Sin Bloqueos Geográficos**: VPN incluida para evitar restricciones -- 📱 **Android TV**: Optimizado para Android TV y control remoto -- ⚡ **Alto Rendimiento**: DNS over HTTPS con fallback +- **Streaming**: Reproducción de canales en vivo +- **VPN Integrada**: Con Mullvad WireGuard - sin app externa +- **Android TV**: Optimizado para Android TV y control remoto +- **DNS over HTTPS**: Múltiples servidores DNS con fallback automático -## 🚀 Instalación +## Instalación ### Opción 1: Descargar APK -Descargar el APK más reciente de [Releases](https://gitea.cbcren.online/renato97/furbo-vpn-edition/releases) +Descargar el APK desde la sección de Releases de este repositorio. ### Opción 2: Compilar ```bash # Clonar repositorio -git clone https://gitea.cbcren.online/renato97/furbo-vpn-edition.git -cd furbo-vpn-edition +git clone +cd # Compilar ./gradlew assembleDebug @@ -31,7 +29,7 @@ cd furbo-vpn-edition # APK en: app/build/outputs/apk/debug/app-debug.apk ``` -## 🔧 Uso de la VPN +## Uso de la VPN 1. **Primera vez**: Tocar el botón "Conectar VPN" en el menú 2. **Permiso**: Aceptar el permiso de VPN cuando se solicite @@ -43,34 +41,35 @@ Para cambiar el servidor Mullvad: 2. Reemplazar el archivo `app/src/main/res/raw/mullvad.conf` 3. Recompilar -## 📋 Requisitos +## Requisitos - **Android**: 7.0 (API 24) o superior - **VPN**: WireGuard integrado (no requiere app externa) -## 🔐 Privacidad +## Configuración de Actualizaciones -- **DNS**: Múltiples servidores DoH (Google, Cloudflare, AdGuard, Quad9) -- **VPN**: Mullvad - Sin logs, sin datos personales -- **Todo en uno**: No necesita otras apps +Para habilitar las actualizaciones automáticas, configura la URL en `app/build.gradle`: -## 📁 Estructura +```gradle +buildConfigField "String", "UPDATE_CHECK_URL", '"https://your-gitea-instance.com/api/v1/repos/user/repo/releases/latest"' +``` + +Opcionalmente, configura un token de API en `UpdateManager.java` si tu repositorio es privado. + +## Estructura ``` app/src/main/ ├── java/com/streamplayer/ │ ├── MainActivity.java # UI principal │ ├── VpnManager.java # Gestión VPN +│ ├── UpdateManager.java # Sistema de actualizaciones │ └── ... ├── res/raw/ │ └── mullvad.conf # Configuración VPN └── AndroidManifest.xml ``` -## 📞 Soporte +## Disclaimer -- Issues: [GitHub Issues](https://gitea.cbcren.online/renato97/furbo-vpn-edition/issues) - ---- - -⚠️ **Disclaimer**: Esta aplicación es para fines educativos. El usuario es responsable de cumplir con los términos de servicio de las plataformas de streaming. +Esta aplicación es para fines educativos. El usuario es responsable de cumplir con los términos de servicio de las plataformas de streaming. diff --git a/app/build.gradle b/app/build.gradle index ffbb3f9..ee7bed8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ android { targetSdk 35 versionCode 100111 versionName "10.1.11" - buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"' + buildConfigField "String", "UPDATE_CHECK_URL", '""' } buildTypes { diff --git a/app/build_simple.gradle b/app/build_simple.gradle deleted file mode 100644 index 7f9f732..0000000 --- a/app/build_simple.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 28 - buildToolsVersion "28.0.3" - - defaultConfig { - applicationId "com.streamplayer" - minSdkVersion 21 - targetSdkVersion 28 - versionCode 1 - versionName "1.0" - } - - buildTypes { - release { - minifyEnabled false - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation 'com.google.android.exoplayer:exoplayer:2.8.4' - implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.2' -} \ No newline at end of file diff --git a/app/src/main/java/com/streamplayer/DeviceRegistry.java b/app/src/main/java/com/streamplayer/DeviceRegistry.java deleted file mode 100644 index d9de21e..0000000 --- a/app/src/main/java/com/streamplayer/DeviceRegistry.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.streamplayer; - -import android.content.Context; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.util.Locale; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - -/** - * Informa al dashboard qué dispositivos tienen instalada la app y permite bloquearlos remotamente. - */ -public class DeviceRegistry { - - public interface Callback { - void onAllowed(); - - void onBlocked(String reason, String tokenPart); - - void onError(String message); - } - - private static final String TAG = "DeviceRegistry"; - private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); - - private final Context appContext; - private final OkHttpClient httpClient; - private final ExecutorService executorService; - private final Handler mainHandler = new Handler(Looper.getMainLooper()); - - public DeviceRegistry(Context context) { - this.appContext = context.getApplicationContext(); - // Usar NetworkUtils para obtener cliente con DNS over HTTPS configurado - this.httpClient = NetworkUtils.getClient(); - this.executorService = Executors.newSingleThreadExecutor(); - } - - public void syncDevice(Callback callback) { - if (TextUtils.isEmpty(BuildConfig.DEVICE_REGISTRY_URL)) { - postAllowed(callback); - return; - } - executorService.execute(() -> { - try { - JSONObject payload = new JSONObject(); - payload.put("deviceId", getDeviceId()); - payload.put("deviceName", Build.MODEL); - payload.put("model", Build.MODEL); - payload.put("manufacturer", capitalize(Build.MANUFACTURER)); - payload.put("osVersion", Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ")"); - payload.put("appVersionName", BuildConfig.VERSION_NAME); - payload.put("appVersionCode", BuildConfig.VERSION_CODE); - - String endpoint = sanitizeBaseUrl(BuildConfig.DEVICE_REGISTRY_URL) + "/api/devices/register"; - RequestBody body = RequestBody.create(payload.toString(), JSON); - Request request = new Request.Builder() - .url(endpoint) - .post(body) - .build(); - try (Response response = httpClient.newCall(request).execute()) { - if (!response.isSuccessful() || response.body() == null) { - throw new IOException("HTTP " + response.code()); - } - String responseText = response.body().string(); - - // Validar que no sea HTML antes de parsear - if (responseText != null) { - String trimmed = responseText.trim(); - if (trimmed.startsWith(" callback.onBlocked(reasonText, token)); - } - - private void postError(Callback callback, String message) { - if (callback == null) { - return; - } - mainHandler.post(() -> callback.onError(message)); - } -} diff --git a/app/src/main/java/com/streamplayer/MainActivity.java b/app/src/main/java/com/streamplayer/MainActivity.java index cd77869..3f64c6f 100644 --- a/app/src/main/java/com/streamplayer/MainActivity.java +++ b/app/src/main/java/com/streamplayer/MainActivity.java @@ -52,8 +52,6 @@ public class MainActivity extends AppCompatActivity { private SectionEntry currentSection; private UpdateManager updateManager; private AlertDialog updateDialog; - private AlertDialog blockedDialog; - private DeviceRegistry deviceRegistry; @Override protected void onCreate(Bundle savedInstanceState) { @@ -122,28 +120,6 @@ public class MainActivity extends AppCompatActivity { Toast.LENGTH_SHORT).show(); } }); - - deviceRegistry = new DeviceRegistry(this); - deviceRegistry.syncDevice(new DeviceRegistry.Callback() { - @Override - public void onAllowed() { - // Device authorized, continue normally. - } - - @Override - public void onBlocked(String reason, String tokenPart) { - showBlockedDialog(reason, tokenPart); - } - - @Override - public void onError(String message) { - if (!TextUtils.isEmpty(message)) { - Toast.makeText(MainActivity.this, - getString(R.string.device_registry_error, message), - Toast.LENGTH_SHORT).show(); - } - } - }); } @Override @@ -160,15 +136,9 @@ public class MainActivity extends AppCompatActivity { if (updateDialog != null && updateDialog.isShowing()) { updateDialog.dismiss(); } - if (blockedDialog != null && blockedDialog.isShowing()) { - blockedDialog.dismiss(); - } if (updateManager != null) { updateManager.release(); } - if (deviceRegistry != null) { - deviceRegistry.release(); - } } private void selectSection(int index) { @@ -348,54 +318,6 @@ public class MainActivity extends AppCompatActivity { } } - private void showBlockedDialog(String reason, String tokenPart) { - if (isFinishing()) { - return; - } - String finalReason = TextUtils.isEmpty(reason) - ? getString(R.string.device_blocked_default_reason) - : reason; - if (blockedDialog != null && blockedDialog.isShowing()) { - blockedDialog.dismiss(); - } - View dialogView = getLayoutInflater().inflate(R.layout.dialog_blocked, null); - TextView messageText = dialogView.findViewById(R.id.blocked_message_text); - View tokenContainer = dialogView.findViewById(R.id.blocked_token_container); - TextView tokenValue = dialogView.findViewById(R.id.blocked_token_value); - messageText.setText(getString(R.string.device_blocked_message, finalReason)); - boolean hasToken = !TextUtils.isEmpty(tokenPart); - if (hasToken) { - tokenContainer.setVisibility(View.VISIBLE); - tokenValue.setText(tokenPart); - tokenValue.setOnClickListener(v -> copyTokenToClipboard(tokenPart)); - } else { - tokenContainer.setVisibility(View.GONE); - } - AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.ThemeOverlay_StreamPlayer_AlertDialog) - .setTitle(R.string.device_blocked_title) - .setView(dialogView) - .setCancelable(false) - .setPositiveButton(R.string.device_blocked_close, - (dialog, which) -> finish()); - if (hasToken) { - builder.setNeutralButton(R.string.device_blocked_copy_token, - (dialog, which) -> copyTokenToClipboard(tokenPart)); - } - blockedDialog = builder.create(); - blockedDialog.show(); - } - - private void copyTokenToClipboard(String tokenPart) { - ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - if (clipboard == null) { - Toast.makeText(this, R.string.device_blocked_copy_error, Toast.LENGTH_SHORT).show(); - return; - } - ClipData data = ClipData.newPlainText("token", tokenPart); - clipboard.setPrimaryClip(data); - Toast.makeText(this, R.string.device_blocked_copy_success, Toast.LENGTH_SHORT).show(); - } - private void toggleVpn() { VpnManager vpnManager = VpnManager.getInstance(); if (vpnManager.isConnected()) { diff --git a/app/src/main/java/com/streamplayer/UpdateManager.java b/app/src/main/java/com/streamplayer/UpdateManager.java index 276d889..30bb8d7 100644 --- a/app/src/main/java/com/streamplayer/UpdateManager.java +++ b/app/src/main/java/com/streamplayer/UpdateManager.java @@ -43,9 +43,12 @@ import okhttp3.Response; public class UpdateManager { private static final String TAG = "UpdateManager"; + // NOTE: Configure tu propia URL de releases en build.gradle o aquí private static final String LATEST_RELEASE_URL = - "https://gitea.cbcren.online/api/v1/repos/renato97/app/releases/latest"; - private static final String GITEA_TOKEN = "4b94b3610136529861af0821040a801906821a0f"; + BuildConfig.UPDATE_CHECK_URL.isEmpty() + ? "https://your-gitea-instance.com/api/v1/repos/your-user/your-repo/releases/latest" + : BuildConfig.UPDATE_CHECK_URL; + private static final String GITEA_TOKEN = ""; // Configura tu token si es necesario private final Context appContext; private final Handler mainHandler; diff --git a/release/update-manifest.example.json b/release/update-manifest.example.json index 77dc329..5089450 100644 --- a/release/update-manifest.example.json +++ b/release/update-manifest.example.json @@ -1,10 +1,10 @@ { - "versionCode": 90000, - "versionName": "9.0.0", - "minSupportedVersionCode": 80000, + "versionCode": 100111, + "versionName": "10.1.11", + "minSupportedVersionCode": 100000, "forceUpdate": false, - "downloadUrl": "https://gitea.cbcren.online/renato97/app/releases/download/v9.0/StreamPlayer-v9.0-DefinitiveEdition.apk", - "fileName": "StreamPlayer-v9.0-DefinitiveEdition.apk", - "sizeBytes": 12000000, - "notes": "Texto opcional si necesitas personalizar las notas que verá el usuario" + "downloadUrl": "https://your-gitea-instance.com/your-user/your-repo/releases/download/v10.1.11/StreamPlayer-v10.1.11.apk", + "fileName": "StreamPlayer-v10.1.11.apk", + "sizeBytes": 20000000, + "notes": "StreamPlayer VPN Edition\n\nConfigura UPDATE_CHECK_URL en app/build.gradle para habilitar actualizaciones automáticas." } diff --git a/update-manifest.json b/update-manifest.json index dc50d5a..054e0cd 100644 --- a/update-manifest.json +++ b/update-manifest.json @@ -1,10 +1,10 @@ { - "versionCode": 100110, - "versionName": "10.1.10", - "minSupportedVersionCode": 0, + "versionCode": 100111, + "versionName": "10.1.11", + "minSupportedVersionCode": 100000, "forceUpdate": false, - "downloadUrl": "http://gitea.cbcren.online/renato97/app/releases/download/v10.1.10/StreamPlayer-v10.1.10-debug.apk", - "fileName": "StreamPlayer-v10.1.10-debug.apk", - "sizeBytes": 9113609, - "notes": "Cambiar a HTTP para evitar errores de certificado" + "downloadUrl": "", + "fileName": "", + "sizeBytes": 0, + "notes": "StreamPlayer VPN Edition - Configura tu propia URL de actualizaciones en build.gradle" }