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:
19
README.md
19
README.md
@@ -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).
|
||||
```
|
||||
|
||||
|
||||
|
||||
19
README_FR.md
19
README_FR.md
@@ -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 s’ils 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).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user