1
0
forked from Mirrors/RGSX

v2.2.0.8 - test feat: add AllDebrid API support as fallback for 1Fichier downloads and update documentation

This commit is contained in:
skymike03
2025-09-11 02:41:43 +02:00
parent ea565f7c46
commit 74ef8b4bdf
13 changed files with 240 additions and 95 deletions

View File

@@ -5,7 +5,7 @@
RGSX is a Python application developed using Pygame for graphics, created for the community by RetroGameSets. It is completely free.
The application supports multiple sources like myrient and 1fichier. These sources can be updated frequently.
The application supports multiple sources like myrient and 1fichier (with optional AllDebrid unlocking fallback). These sources can be updated frequently.
---
@@ -21,7 +21,7 @@ RGSX also offers a headless command-line interface to list platforms/games and d
- **Game downloads** : Support for ZIP files and handling of unsupported extensions based on EmulationStation's `es_systems.cfg` (and custom `es_systems_*.cfg` on Batocera). RGSX reads allowed extensions per system from these configs and will automatically extract archives when a system doesn't support them.
- Downloads require no authentication or account for most sources.
- Systems marked `(1fichier)` in the name will only be accessible if you provide your 1fichier API key (see below).
- Systems marked `(1fichier)` in the name will only be accessible if you provide either your 1Fichier API key or an AllDebrid API key (see below).
- **Download history** : View and re-download previous files.
- **Multi-select downloads** : Mark multiple games in the game list with the key mapped to Clear History (default X) to enqueue several downloads in one batch. Press Confirm to start batch.
- **Control customization** : Remap keyboard or controller keys to your preference with automatic button name detection from EmulationStation (beta).
@@ -80,10 +80,14 @@ You will find RGSX in the "PORTS" system or "Homebrew and ports" and in `/roms/p
## 🏁 First startup
---
> ## IMPORTANT
> If you have a 1Fichier API key, you must enter it in
> `/saves/ports/rgsx/1FichierAPI.txt`
> if you want to download from 1Fichier links.
> ## IMPORTANT (1Fichier / AllDebrid)
> To download from 1Fichier links, you can use either your 1Fichier API key or an AllDebrid API key (automatic fallback if 1Fichier is missing).
>
> Where to paste your API key (file should contain the key only):
> - `/saves/ports/rgsx/1FichierAPI.txt` (1Fichier API key)
> - `/saves/ports/rgsx/AllDebridAPI.txt` (AllDebrid API key)
>
> Do NOT create these files manually. Start a 1Fichier download once: RGSX will auto-create empty files if missing. Then open the appropriate file and paste your key.
---
- Launch RGSX from ports on batocera, from Windows on Retrobat.
@@ -185,7 +189,8 @@ RGSX/
├── controls.json # Control mapping file (generated after first startup).
├── history.json # Download history database (generated after first download).
├── rom_extensions.json # Generated from es_systems.cfg: per-system allowed ROM extensions cache.
── 1FichierAPI.txt # 1fichier API key (premium account and + only) (empty by default).
── 1FichierAPI.txt # 1fichier API key (premium account and + only) (empty by default).
└── AllDebridAPI.txt # AllDebrid API key (optional, fallback for 1Fichier links) (empty by default).
```

View File

@@ -4,7 +4,7 @@
RGSX est une application développée en Python basée sur Pygame pour la partie graphique pour la communauté par RetroGameSets. Elle est entièrement gratuite.
L'application prend en charge plusieurs sources comme myrient, 1fichier. Ces sources pourront être mises à jour fréquemment.
L'application prend en charge plusieurs sources comme myrient, 1fichier (avec support de débridage via AllDebrid en option). Ces sources pourront être mises à jour fréquemment.
---
@@ -20,7 +20,7 @@ RGSX propose aussi une interface en ligne de commande (sans interface graphique)
- **Téléchargement de jeux** : Prise en charge des fichiers ZIP et gestion des extensions non supportées à partir du fichier `es_systems.cfg` d'EmulationStation (et des `es_systems_*.cfg` personnalisés sur Batocera). RGSX lit les extensions autorisées par système depuis ces configurations et extrait automatiquement les archives si le système ne les supporte pas.
- Les téléchargements ne nécessitent aucune authentification ni compte pour la plupart.
- Les systèmes notés `(1fichier)` dans le nom ne seront accessibles que si vous renseignez votre clé API 1fichier (voir plus bas).
- Les systèmes notés `(1fichier)` dans le nom ne seront accessibles que si vous renseignez votre clé API 1Fichier ou une clé API AllDebrid (voir plus bas).
- **Historique des téléchargements** : Consultez et retéléchargez les anciens fichiers.
- **Téléchargements multi-sélection** : Marquez plusieurs jeux dans la liste avec la touche associée à Vider Historique (par défaut X) pour préparer un lot. Appuyez ensuite sur Confirmer pour lancer les téléchargements en séquence.
- **Personnalisation des contrôles** : Remappez les touches du clavier ou de la manette à votre convenance avec détection automatique des noms de boutons depuis EmulationStation(beta).
@@ -78,10 +78,14 @@ Vous trouverez RGSX dans le système "PORTS" ou "Jeux Amateurs et portages" et d
## 🏁 Premier démarrage
---
> ## IMPORTANT
> Si vous avez une clé API 1Fichier, vous devez la renseigner dans
> `/saves/ports/rgsx/1FichierAPI.txt`
> si vous souhaitez télécharger depuis des liens 1Fichier.
> ## IMPORTANT (1Fichier / AllDebrid)
> Pour télécharger depuis des liens 1Fichier, vous pouvez utiliser soit votre clé API 1Fichier, soit votre clé API AllDebrid (fallback automatique si 1Fichier est absent).
>
> Où coller votre clé API (le fichier doit contenir uniquement la clé) :
> - `/saves/ports/rgsx/1FichierAPI.txt` (clé API 1Fichier)
> - `/saves/ports/rgsx/AllDebridAPI.txt` (clé API AllDebrid)
>
> Ne créez PAS ces fichiers manuellement. Lancez une première fois un téléchargement 1Fichier: RGSX créera automatiquement les fichiers vides sils sont absents. Ensuite, ouvrez le fichier correspondant et collez votre clé.
---
- Lancez RGSX depuis ports sur batocera, depuis Windows sur Retrobat.
@@ -209,7 +213,8 @@ RGSX/
├── controls.json # Fichier de mappage des contrôles (généré après le premier démarrage).
├── history.json # Base de données de l'historique de téléchargements (généré après le premier téléchargement).
├── rom_extensions.json # Généré depuis es_systems.cfg : cache des extensions autorisées par système.
── 1FichierAPI.txt # Clé API 1fichier (compte premium et + uniquement) (vide par défaut).
── 1FichierAPI.txt # Clé API 1fichier (compte premium et + uniquement) (vide par défaut).
└── AllDebridAPI.txt # Clé API AllDebrid (optionnelle, fallback pour les liens 1Fichier) (vide par défaut).
```
---

View File

@@ -570,18 +570,27 @@ async def main():
config.current_history_item = len(config.history) - 1 # Sélectionner l'entrée en cours
if is_1fichier_url(url):
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
from utils import load_api_key_alldebrid
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.previous_menu_state = config.menu_state
config.menu_state = "error"
config.error_message = (
f"Attention il faut renseigner sa clé API (premium only) dans le fichier {os.path.join(config.SAVE_FOLDER, '1fichierAPI.txt')}"
)
try:
both_paths = f"{os.path.join(config.SAVE_FOLDER,'1FichierAPI.txt')} or {os.path.join(config.SAVE_FOLDER,'AllDebridAPI.txt')}"
config.error_message = _("error_api_key").format(both_paths)
except Exception:
config.error_message = "Please enter API key (1fichier or AllDebrid)"
# Mettre à jour l'entrée temporaire avec l'erreur
config.history[-1]["status"] = "Erreur"
config.history[-1]["progress"] = 0
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
config.history[-1]["message"] = "API NOT FOUND"
save_history(config.history)
config.needs_redraw = True
logger.error("Clé API 1fichier absente")
logger.error("Clé API 1fichier et AllDebrid absentes")
config.pending_download = None
continue
pending = check_extension_before_download(url, platform_name, game_name)
@@ -670,13 +679,22 @@ async def main():
logger.debug(f"Vérification pour retéléchargement de {game_name}, URL: {url}")
if is_1fichier_url(url):
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
from utils import load_api_key_alldebrid
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.previous_menu_state = config.menu_state
config.menu_state = "error"
config.error_message = (
f"Attention il faut renseigner sa clé API (premium only) dans le fichier {os.path.join(config.SAVE_FOLDER, '1fichierAPI.txt')}"
)
try:
both_paths = f"{os.path.join(config.SAVE_FOLDER,'1FichierAPI.txt')} or {os.path.join(config.SAVE_FOLDER,'AllDebridAPI.txt')}"
config.error_message = _("error_api_key").format(both_paths)
except Exception:
config.error_message = "Please enter API key (1fichier or AllDebrid)"
config.needs_redraw = True
logger.error("Clé API 1fichier absente")
logger.error("Clé API 1fichier et AllDebrid absentes")
config.pending_download = None
continue
pending = check_extension_before_download(url, platform_name, game_name)

View File

@@ -104,7 +104,7 @@ JSON_EXTENSIONS = os.path.join(SAVE_FOLDER, "rom_extensions.json")
PRECONF_CONTROLS_PATH = os.path.join(APP_FOLDER, "assets", "controls")
CONTROLS_CONFIG_PATH = os.path.join(SAVE_FOLDER, "controls.json")
HISTORY_PATH = os.path.join(SAVE_FOLDER, "history.json")
API_KEY_1FICHIER = os.path.join(SAVE_FOLDER, "1fichierAPI.txt")
API_KEY_1FICHIER = os.path.join(SAVE_FOLDER, "1FichierAPI.txt")
RGSX_SETTINGS_PATH = os.path.join(SAVE_FOLDER, "rgsx_settings.json")
# URL

View File

@@ -14,7 +14,7 @@ from network import download_rom, download_from_1fichier, is_1fichier_url, reque
from utils import (
load_games, check_extension_before_download, is_extension_supported,
load_extensions_json, play_random_music, sanitize_filename,
load_api_key_1fichier, save_music_config
load_api_key_1fichier, load_api_key_alldebrid, save_music_config
)
from history import load_history, clear_history, add_to_history, save_history
import logging
@@ -584,8 +584,14 @@ def handle_controls(event, sources, joystick, screen):
if is_1fichier_url(url):
config.API_KEY_1FICHIER = load_api_key_1fichier()
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.history[-1]["status"] = "Erreur"
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
config.history[-1]["message"] = "API NOT FOUND"
save_history(config.history)
continue
task = asyncio.create_task(download_from_1fichier(url, platform, game_name, config.pending_download[3], task_id))
@@ -620,19 +626,26 @@ def handle_controls(event, sources, joystick, screen):
if is_1fichier_url(url):
config.API_KEY_1FICHIER = load_api_key_1fichier()
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.previous_menu_state = config.menu_state
config.menu_state = "error"
try:
config.error_message = _("error_api_key_extended")
both_paths = f"{os.path.join(config.SAVE_FOLDER,'1FichierAPI.txt')} or {os.path.join(config.SAVE_FOLDER,'AllDebridAPI.txt')}"
config.error_message = _("error_api_key").format(both_paths)
except Exception as e:
logger.error(f"Erreur lors de la traduction de error_api_key_extended: {str(e)}")
config.error_message = "Missing 1fichier API key" # Message de secours
logger.error(f"Erreur lors de la traduction de error_api_key: {str(e)}")
config.error_message = "Please enter API key (1fichier or AllDebrid)"
config.history[-1]["status"] = "Erreur"
config.history[-1]["progress"] = 0
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
config.history[-1]["message"] = "API NOT FOUND"
save_history(config.history)
config.needs_redraw = True
logger.error("Clé API 1fichier absente, téléchargement impossible.")
logger.error("Clé API 1fichier et AllDebrid absentes, téléchargement impossible.")
config.pending_download = None
return action
config.pending_download = check_extension_before_download(url, platform, game_name)
@@ -734,17 +747,25 @@ def handle_controls(event, sources, joystick, screen):
config.current_history_item = len(config.history) - 1
if is_1fichier_url(url):
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.previous_menu_state = config.menu_state
config.menu_state = "error"
config.error_message = _(
"error_api_key"
).format(os.path.join(config.SAVE_FOLDER,"1fichierAPI.txt"))
try:
both_paths = f"{os.path.join(config.SAVE_FOLDER,'1FichierAPI.txt')} or {os.path.join(config.SAVE_FOLDER,'AllDebridAPI.txt')}"
config.error_message = _("error_api_key").format(both_paths)
except Exception:
config.error_message = "Please enter API key (1fichier or AllDebrid)"
config.history[-1]["status"] = "Erreur"
config.history[-1]["progress"] = 0
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
config.history[-1]["message"] = "API NOT FOUND"
save_history(config.history)
config.needs_redraw = True
logger.error("Clé API 1fichier absente, téléchargement impossible.")
logger.error("Clé API 1fichier et AllDebrid absentes, téléchargement impossible.")
config.pending_download = None
return action
task_id = str(pygame.time.get_ticks())
@@ -800,8 +821,14 @@ def handle_controls(event, sources, joystick, screen):
if is_1fichier_url(url):
config.API_KEY_1FICHIER = load_api_key_1fichier()
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.history[-1]["status"] = "Erreur"
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
config.history[-1]["message"] = "API NOT FOUND"
save_history(config.history)
continue
task = asyncio.create_task(download_from_1fichier(url, platform, game_name, config.pending_download[3], task_id))
@@ -865,8 +892,14 @@ def handle_controls(event, sources, joystick, screen):
if is_1fichier_url(url):
config.API_KEY_1FICHIER = load_api_key_1fichier()
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.history[-1]["status"] = "Erreur"
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
config.history[-1]["message"] = "API NOT FOUND"
save_history(config.history)
continue
task = asyncio.create_task(download_from_1fichier(url, platform, game_name, config.pending_download[3], task_id))
@@ -960,17 +993,27 @@ def handle_controls(event, sources, joystick, screen):
task_id = str(pygame.time.get_ticks())
if is_1fichier_url(url):
if not config.API_KEY_1FICHIER:
# Fallback AllDebrid
try:
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
except Exception:
config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "")
if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""):
config.previous_menu_state = config.menu_state
config.menu_state = "error"
logger.warning("clé api absente dans os.path.join(config.SAVE_FOLDER, '1fichierAPI.txt')\n")
config.error_message = _("error_api_key").format(os.path.join(config.SAVE_FOLDER, "1fichierAPI.txt"))
logger.warning("clé api absente pour 1fichier et AllDebrid")
try:
both_paths = f"{os.path.join(config.SAVE_FOLDER,'1FichierAPI.txt')} or {os.path.join(config.SAVE_FOLDER,'AllDebridAPI.txt')}"
config.error_message = _("error_api_key").format(both_paths)
except Exception:
config.error_message = "Please enter API key (1fichier or AllDebrid)"
config.history[-1]["status"] = "Erreur"
config.history[-1]["progress"] = 0
config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente"
config.history[-1]["message"] = "API NOT FOUND"
save_history(config.history)
config.needs_redraw = True
logger.error("Clé API 1fichier absente, retéléchargement impossible.")
logger.error("Clé API 1fichier et AllDebrid absentes, retéléchargement impossible.")
config.pending_download = None
return action
task = asyncio.create_task(download_from_1fichier(url, platform, game_name, is_zip_non_supported, task_id))

View File

@@ -24,7 +24,7 @@
"error_no_internet": "Keine Internetverbindung. Überprüfe dein Netzwerk.",
"error_controls_mapping": "Fehler beim Zuordnen der Steuerung",
"error_api_key": "Achtung, du musst deinen API-Schlüssel (nur Premium) in der Datei {0} eingeben",
"error_api_key_extended": "Achtung, du musst deinen API-Schlüssel (nur Premium) in der Datei /userdata/saves/ports/rgsx/1fichierAPI.txt einfügen. Öffne die Datei in einem Texteditor und füge den API-Schlüssel ein",
"error_api_key_extended": "Achtung, du musst deinen API-Schlüssel (nur Premium) in der Datei /userdata/saves/ports/rgsx/1FichierAPI.txt einfügen. Öffne die Datei in einem Texteditor und füge den API-Schlüssel ein",
"error_invalid_download_data": "Ungültige Downloaddaten",
"error_delete_sources": "Fehler beim Löschen der Datei systems_list.json oder Ordner",
"error_extension": "Nicht unterstützte Erweiterung oder Downloadfehler",

View File

@@ -24,7 +24,7 @@
"error_no_internet": "No Internet connection. Check your network.",
"error_controls_mapping": "Failed to map controls",
"error_api_key": "Please enter your API key (premium only) in the file {0}",
"error_api_key_extended": "Please enter your API key (premium only) in the file /userdata/saves/ports/rgsx/1fichierAPI.txt by opening it in a text editor and pasting your API key",
"error_api_key_extended": "Please enter your API key (premium only) in the file /userdata/saves/ports/rgsx/1FichierAPI.txt by opening it in a text editor and pasting your API key",
"error_invalid_download_data": "Invalid download data",
"error_delete_sources": "Error deleting systems_list.json file or folders",
"error_extension": "Unsupported extension or download error",

View File

@@ -25,7 +25,7 @@
"error_no_internet": "Sin conexión a Internet. Verifica tu red.",
"error_controls_mapping": "Error al mapear los controles",
"error_api_key": "Atención, debes ingresar tu clave API (solo premium) en el archivo {0}",
"error_api_key_extended": "Atención, debes ingresar tu clave API (solo premium) en el archivo /userdata/saves/ports/rgsx/1fichierAPI.txt, abrirlo en un editor de texto y pegar la clave API",
"error_api_key_extended": "Atención, debes ingresar tu clave API (solo premium) en el archivo /userdata/saves/ports/rgsx/1FichierAPI.txt, abrirlo en un editor de texto y pegar la clave API",
"error_invalid_download_data": "Datos de descarga no válidos",
"error_delete_sources": "Error al eliminar el archivo systems_list.json o carpetas",
"error_extension": "Extensión no soportada o error de descarga",

View File

@@ -21,7 +21,7 @@
"error_no_internet": "Pas de connexion Internet. Vérifiez votre réseau.",
"error_controls_mapping": "Échec du mappage des contrôles",
"error_api_key": "Attention il faut renseigner sa clé API (premium only) dans le fichier {0}",
"error_api_key_extended": "Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt à ouvrir dans un éditeur de texte et coller la clé API",
"error_api_key_extended": "Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1FichierAPI.txt à ouvrir dans un éditeur de texte et coller la clé API",
"error_invalid_download_data": "Données de téléchargement invalides",
"error_delete_sources": "Erreur lors de la suppression du fichier systems_list.json ou dossiers",
"error_extension": "Extension non supportée ou erreur de téléchargement",

View File

@@ -24,7 +24,7 @@
"error_no_internet": "Nessuna connessione Internet. Controlla la rete.",
"error_controls_mapping": "Impossibile mappare i controlli",
"error_api_key": "Inserisci la tua API key (solo premium) nel file {0}",
"error_api_key_extended": "Inserisci la tua API key (solo premium) nel file /userdata/saves/ports/rgsx/1fichierAPI.txt aprendolo in un editor e incollando la chiave",
"error_api_key_extended": "Inserisci la tua API key (solo premium) nel file /userdata/saves/ports/rgsx/1FichierAPI.txt aprendolo in un editor e incollando la chiave",
"error_invalid_download_data": "Dati di download non validi",
"error_delete_sources": "Errore nell'eliminazione del file systems_list.json o delle cartelle",
"error_extension": "Estensione non supportata o errore di download",

View File

@@ -24,7 +24,7 @@
"error_no_internet": "Sem conexão com a Internet. Verifique sua rede.",
"error_controls_mapping": "Falha ao mapear controles",
"error_api_key": "Insira sua chave API (somente premium) no arquivo {0}",
"error_api_key_extended": "Insira sua chave API (somente premium) no arquivo /userdata/saves/ports/rgsx/1fichierAPI.txt abrindo-o em um editor de texto e colando sua chave",
"error_api_key_extended": "Insira sua chave API (somente premium) no arquivo /userdata/saves/ports/rgsx/1FichierAPI.txt abrindo-o em um editor de texto e colando sua chave",
"error_invalid_download_data": "Dados de download inválidos",
"error_delete_sources": "Erro ao deletar arquivo sources.json ou pastas",
"error_extension": "Extensão não suportada ou erro no download",

View File

@@ -15,7 +15,7 @@ try:
except Exception:
pygame = None # type: ignore
from config import OTA_VERSION_ENDPOINT,APP_FOLDER, UPDATE_FOLDER, OTA_UPDATE_ZIP
from utils import sanitize_filename, extract_zip, extract_rar, load_api_key_1fichier, normalize_platform_name
from utils import sanitize_filename, extract_zip, extract_rar, load_api_key_1fichier, load_api_key_alldebrid, normalize_platform_name
from history import save_history
import logging
import datetime
@@ -635,6 +635,10 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False, task_id=None):
config.API_KEY_1FICHIER = load_api_key_1fichier()
if not config.API_KEY_1FICHIER:
# Fallback: essayer AllDebrid
config.API_KEY_ALLDEBRID = load_api_key_alldebrid()
logger.debug(f"Clé API 1fichier absente, fallback AllDebrid: {'présente' if config.API_KEY_ALLDEBRID else 'absente'}")
logger.debug(f"Début téléchargement 1fichier: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}, task_id={task_id}")
logger.debug(f"Clé API 1fichier: {'présente' if config.API_KEY_1FICHIER else 'absente'}")
result = [None, None]
@@ -679,50 +683,83 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
logger.error(f"Pas de permission d'écriture dans {dest_dir}")
raise PermissionError(f"Pas de permission d'écriture dans {dest_dir}")
headers = {
"Authorization": f"Bearer {config.API_KEY_1FICHIER}",
"Content-Type": "application/json"
}
payload = {
"url": link,
"pretty": 1
}
logger.debug(f"Préparation requête file/info pour {link}")
response = requests.post("https://api.1fichier.com/v1/file/info.cgi", headers=headers, json=payload, timeout=30)
logger.debug(f"Réponse file/info reçue, code: {response.status_code}")
response.raise_for_status()
file_info = response.json()
if "error" in file_info and file_info["error"] == "Resource not found":
logger.error(f"Le fichier {game_name} n'existe pas sur 1fichier")
result[0] = False
result[1] = _("network_file_not_found").format(game_name)
return
filename = file_info.get("filename", "").strip()
if not filename:
logger.error(f"Impossible de récupérer le nom du fichier")
result[0] = False
result[1] = _("network_cannot_get_filename")
return
sanitized_filename = sanitize_filename(filename)
dest_path = os.path.join(dest_dir, sanitized_filename)
logger.debug(f"Chemin destination: {dest_path}")
logger.debug(f"Envoi requête get_token pour {link}")
response = requests.post("https://api.1fichier.com/v1/download/get_token.cgi", headers=headers, json=payload, timeout=30)
logger.debug(f"Réponse get_token reçue, code: {response.status_code}")
response.raise_for_status()
download_info = response.json()
final_url = download_info.get("url")
if not final_url:
logger.error(f"Impossible de récupérer l'URL de téléchargement")
result[0] = False
result[1] = _("network_cannot_get_download_url")
return
logger.debug(f"URL de téléchargement obtenue: {final_url}")
# Choisir la stratégie d'accès: 1fichier direct via API, sinon AllDebrid pour débrider
if config.API_KEY_1FICHIER:
headers = {
"Authorization": f"Bearer {config.API_KEY_1FICHIER}",
"Content-Type": "application/json"
}
payload = {
"url": link,
"pretty": 1
}
logger.debug(f"Préparation requête 1fichier file/info pour {link}")
response = requests.post("https://api.1fichier.com/v1/file/info.cgi", headers=headers, json=payload, timeout=30)
logger.debug(f"Réponse file/info reçue, code: {response.status_code}")
response.raise_for_status()
file_info = response.json()
if "error" in file_info and file_info["error"] == "Resource not found":
logger.error(f"Le fichier {game_name} n'existe pas sur 1fichier")
result[0] = False
result[1] = _("network_file_not_found").format(game_name)
return
filename = file_info.get("filename", "").strip()
if not filename:
logger.error("Impossible de récupérer le nom du fichier")
result[0] = False
result[1] = _("network_cannot_get_filename")
return
sanitized_filename = sanitize_filename(filename)
dest_path = os.path.join(dest_dir, sanitized_filename)
logger.debug(f"Chemin destination: {dest_path}")
logger.debug(f"Envoi requête 1fichier get_token pour {link}")
response = requests.post("https://api.1fichier.com/v1/download/get_token.cgi", headers=headers, json=payload, timeout=30)
logger.debug(f"Réponse get_token reçue, code: {response.status_code}")
response.raise_for_status()
download_info = response.json()
final_url = download_info.get("url")
if not final_url:
logger.error("Impossible de récupérer l'URL de téléchargement")
result[0] = False
result[1] = _("network_cannot_get_download_url")
return
logger.debug(f"URL de téléchargement obtenue via 1fichier: {final_url}")
else:
# AllDebrid: débrider l'URL 1fichier vers une URL directe
if not getattr(config, 'API_KEY_ALLDEBRID', ''):
logger.error("Aucune clé API (1fichier/AllDebrid) disponible")
result[0] = False
result[1] = _("network_api_error").format("Missing API key") if _ else "API key missing"
return
ad_key = config.API_KEY_ALLDEBRID
# AllDebrid API v4 example: GET https://api.alldebrid.com/v4/link/unlock?agent=<app>&apikey=<key>&link=<url>
params = {
'agent': 'RGSX',
'apikey': ad_key,
'link': link
}
logger.debug("Requête AllDebrid link/unlock en cours")
response = requests.get("https://api.alldebrid.com/v4/link/unlock", params=params, timeout=30)
logger.debug(f"Réponse AllDebrid reçue, code: {response.status_code}")
response.raise_for_status()
ad_json = response.json()
if ad_json.get('status') != 'success':
err = ad_json.get('error', {}).get('code') or ad_json
logger.error(f"AllDebrid échec débridage: {err}")
result[0] = False
result[1] = _("network_api_error").format(f"AllDebrid unlock failed: {err}") if _ else f"AllDebrid unlock failed: {err}"
return
data = ad_json.get('data', {})
filename = data.get('filename') or game_name
final_url = data.get('link') or data.get('download') or data.get('streamingLink')
if not final_url:
logger.error("AllDebrid n'a pas renvoyé de lien direct")
result[0] = False
result[1] = _("network_cannot_get_download_url")
return
sanitized_filename = sanitize_filename(filename)
dest_path = os.path.join(dest_dir, sanitized_filename)
logger.debug(f"URL directe obtenue via AllDebrid: {final_url}")
lock = threading.Lock()
retries = 10
retry_delay = 10
@@ -754,6 +791,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
downloaded = 0
chunk_size = 8192
last_update_time = time.time()
last_downloaded = 0
update_interval = 0.1 # Mettre à jour toutes les 0,1 secondes
logger.debug(f"Ouverture fichier: {dest_path}")
with open(dest_path, 'wb') as f:
@@ -789,8 +827,12 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
entry["total_size"] = total_size
config.needs_redraw = True
break
progress_queues[task_id].put((task_id, downloaded, total_size))
# Calcul de la vitesse en Mo/s
delta = downloaded - last_downloaded
speed = (delta / (current_time - last_update_time) / (1024 * 1024)) if (current_time - last_update_time) > 0 else 0.0
last_downloaded = downloaded
last_update_time = current_time
progress_queues[task_id].put((task_id, downloaded, total_size, speed))
if is_zip_non_supported:
with lock:
@@ -893,7 +935,11 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
logger.debug(f"Mise à jour finale historique: status={entry['status']}, progress={entry['progress']}%, message={message}, task_id={task_id}")
break
else:
downloaded, total_size = data[1], data[2]
if len(data) >= 4:
downloaded, total_size, speed = data[1], data[2], data[3]
else:
downloaded, total_size = data[1], data[2]
speed = 0.0
progress_percent = int(downloaded / total_size * 100) if total_size > 0 else 0
progress_percent = max(0, min(100, progress_percent))
@@ -904,6 +950,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
entry["status"] = "Téléchargement"
entry["downloaded_size"] = downloaded
entry["total_size"] = total_size
entry["speed"] = speed # Ajout de la vitesse
config.needs_redraw = True
break
await asyncio.sleep(0.1)

View File

@@ -1284,6 +1284,33 @@ def load_api_key_1fichier():
logger.error(f"Erreur lors de la lecture de la clé API : {e}")
return ""
def load_api_key_alldebrid():
"""Charge la clé API AllDebrid depuis le dossier de sauvegarde, crée le fichier si absent."""
try:
api_file = os.path.join(config.SAVE_FOLDER, "AllDebridAPI.txt")
logger.debug(f"Chemin du fichier de clé API AllDebrid: {api_file}")
if not os.path.exists(api_file):
logger.info("Fichier de clé API AllDebrid non trouvé")
os.makedirs(config.SAVE_FOLDER, exist_ok=True)
with open(api_file, "w", encoding="utf-8") as f:
f.write("")
logger.info(f"Fichier de clé API AllDebrid créé : {api_file}")
return ""
with open(api_file, "r", encoding="utf-8") as f:
api_key = f.read().strip()
logger.debug(f"Clé API AllDebrid lue: '{api_key}' (longueur: {len(api_key)})")
if not api_key:
logger.warning("Clé API AllDebrid vide, renseignez-la dans AllDebridAPI.txt pour activer le débridage.")
# Stocke dans la config pour usage global
try:
config.API_KEY_ALLDEBRID = api_key
except Exception:
pass
return api_key
except Exception as e:
logger.error(f"Erreur lors du chargement de la clé API AllDebrid: {e}")
return ""
def load_music_config():
"""Charge la configuration musique depuis rgsx_settings.json."""
try: