Remove dashboard/VPS references - make project PR-ready
- Remove DeviceRegistry.java (dashboard integration) - Remove VPS IP from build.gradle - Remove personal Gitea token from UpdateManager - Add configurable UPDATE_CHECK_URL for updates - Clean README to be generic and PR-ready - Clean update manifests - Remove Docker files and .env from repo
This commit is contained in:
4
.env
4
.env
@@ -1,4 +0,0 @@
|
||||
GITEA_TOKEN=efeed2af00597883adb04da70bd6a7c2993ae92d
|
||||
GEMINI_API_KEY=AIzaSyDWOgyAJqscuPU6iSpS6gxupWBm4soNw5o
|
||||
TELEGRAM_BOT_TOKEN=8593525164:AAGCX9B_RJGN35_F7tSB72rEZhS_4Zpcszs
|
||||
TELEGRAM_CHAT_ID=692714536
|
||||
57
Dockerfile
57
Dockerfile
@@ -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"]
|
||||
@@ -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
|
||||
53
README.md
53
README.md
@@ -1,29 +1,27 @@
|
||||
# 🏆 Furbo VPN Edition
|
||||
# StreamPlayer VPN Edition
|
||||
|
||||
[](https://android.com)
|
||||
[]()
|
||||
[](LICENSE)
|
||||
[](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 <repo-url>
|
||||
cd <project-dir>
|
||||
|
||||
# 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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
@@ -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("<!") || trimmed.startsWith("<html")) {
|
||||
throw new IOException("El servidor devolvió HTML en lugar de JSON");
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject json = new JSONObject(responseText);
|
||||
JSONObject deviceJson = json.optJSONObject("device");
|
||||
JSONObject verificationJson = json.optJSONObject("verification");
|
||||
boolean blocked = json.optBoolean("blocked", false);
|
||||
String reason = json.optString("message");
|
||||
if (TextUtils.isEmpty(reason) && deviceJson != null) {
|
||||
reason = deviceJson.optString("notes", "");
|
||||
}
|
||||
String tokenPart = "";
|
||||
if (verificationJson != null) {
|
||||
boolean verificationRequired = verificationJson.optBoolean("required", false);
|
||||
blocked = blocked || verificationRequired;
|
||||
tokenPart = verificationJson.optString("clientTokenPart", "");
|
||||
}
|
||||
if (blocked) {
|
||||
postBlocked(callback, reason, tokenPart);
|
||||
} else {
|
||||
postAllowed(callback);
|
||||
}
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
Log.w(TAG, "Device sync error", e);
|
||||
postError(callback, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String sanitizeBaseUrl(String base) {
|
||||
if (TextUtils.isEmpty(base)) {
|
||||
return "";
|
||||
}
|
||||
if (base.endsWith("/")) {
|
||||
return base.substring(0, base.length() - 1);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
private String getDeviceId() {
|
||||
String id = Settings.Secure.getString(appContext.getContentResolver(),
|
||||
Settings.Secure.ANDROID_ID);
|
||||
if (TextUtils.isEmpty(id)) {
|
||||
id = Build.MODEL + "-" + Build.BOARD + "-" + BuildConfig.VERSION_CODE;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private String capitalize(String value) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
return "";
|
||||
}
|
||||
return value.substring(0, 1).toUpperCase(Locale.getDefault())
|
||||
+ value.substring(1);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
|
||||
private void postAllowed(Callback callback) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
mainHandler.post(callback::onAllowed);
|
||||
}
|
||||
|
||||
private void postBlocked(Callback callback, String reason, String tokenPart) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
String reasonText = reason == null ? "" : reason;
|
||||
String token = tokenPart == null ? "" : tokenPart;
|
||||
mainHandler.post(() -> callback.onBlocked(reasonText, token));
|
||||
}
|
||||
|
||||
private void postError(Callback callback, String message) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
mainHandler.post(() -> callback.onError(message));
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user