mirror of
https://github.com/RetroGameSets/RGSX.git
synced 2026-05-19 07:43:35 +02:00
Compare commits
41 Commits
v2.4.0.1
...
v2.6.1.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7dad84108 | ||
|
|
c9f48d20dd | ||
|
|
cd7795f70e | ||
|
|
6813a0bc3d | ||
|
|
21b39c66b9 | ||
|
|
42b2204aeb | ||
|
|
67a38c45aa | ||
|
|
893b73ecc5 | ||
|
|
5e1a684275 | ||
|
|
9226a818f3 | ||
|
|
2fd1bcaf01 | ||
|
|
875bf8fa23 | ||
|
|
f9cbf0196e | ||
|
|
eb86d69895 | ||
|
|
b09b3da371 | ||
|
|
0915a90fbe | ||
|
|
3ae3c151eb | ||
|
|
7460b12d71 | ||
|
|
2f437c1aa4 | ||
|
|
054b174c18 | ||
|
|
fbb1a2aa68 | ||
|
|
1dbc741617 | ||
|
|
c4913a5fc2 | ||
|
|
bf9d3d2de5 | ||
|
|
9979949bdc | ||
|
|
9ed264544f | ||
|
|
779c060927 | ||
|
|
88400e538f | ||
|
|
cbab067dd6 | ||
|
|
b4ed0b355d | ||
|
|
51ad08ff33 | ||
|
|
d6a5c4b27e | ||
|
|
2c7c3414a5 | ||
|
|
059d3988ac | ||
|
|
50c9b9caad | ||
|
|
7d2d55fe5f | ||
|
|
14a5416d2d | ||
|
|
3193dc90f6 | ||
|
|
b437f31854 | ||
|
|
08f3e64d2a | ||
|
|
4968af2da9 |
127
.github/workflows/release.yml
vendored
127
.github/workflows/release.yml
vendored
@@ -61,54 +61,103 @@ jobs:
|
||||
echo "✓ All packages created successfully"
|
||||
ls -lh dist/
|
||||
|
||||
- name: Generate release notes with commit message
|
||||
shell: bash
|
||||
run: |
|
||||
# Récupérer le message de commit associé au tag
|
||||
COMMIT_MSG=$(git log -1 --format=%B ${{ github.ref_name }})
|
||||
echo "Commit message:"
|
||||
echo "$COMMIT_MSG"
|
||||
|
||||
# Créer le fichier de release notes
|
||||
cat > dist/RELEASE_NOTES.md << 'RELEASE_EOF'
|
||||
# 📦 RGSX Release ${{ github.ref_name }}
|
||||
|
||||
## 📝 Changelog
|
||||
RELEASE_EOF
|
||||
|
||||
# Ajouter le message de commit
|
||||
echo "$COMMIT_MSG" >> dist/RELEASE_NOTES.md
|
||||
|
||||
# Ajouter le reste des instructions
|
||||
cat >> dist/RELEASE_NOTES.md << 'RELEASE_EOF'
|
||||
|
||||
---
|
||||
|
||||
## 📥 Automatic Installation (Only for Batocera/Knulli)
|
||||
|
||||
### ON PC, NUC, SteamDeck or any x86_64 based:
|
||||
1. Open File Manager (F1) then "Applications" and launch xterm
|
||||
2. Use the command line `curl -L bit.ly/rgsx-install | sh`
|
||||
3. Launch RGSX from "Ports" menu
|
||||
|
||||
### ON RASPBERRY/ ARM based SBC / HANDHELD :
|
||||
1. Connect your device with SSH on a computer/smartphone connected to same network (ssh root@IPADDRESS , pass:linux)
|
||||
2. Use the command line `curl -L bit.ly/rgsx-install | sh`
|
||||
3. Launch RGSX from "Ports" menu
|
||||
|
||||
## 📥 Manual Installation
|
||||
|
||||
### Batocera/Knulli
|
||||
1. Download latest release : https://github.com/RetroGameSets/RGSX/releases/latest/download/RGSX_full_latest.zip
|
||||
2. Extract only PORTS folder in `/userdata/roms/`
|
||||
3. Launch RGSX from the Ports menu
|
||||
|
||||
### Retrobat/Full Installation on Windows
|
||||
1. Download latest release : https://github.com/RetroGameSets/RGSX/releases/latest/download/RGSX_full_latest.zip
|
||||
2. Extract all folders in your Retrobat\roms folder
|
||||
3. Launch RGSX from system "Windows"
|
||||
|
||||
|
||||
## 📥 Manual Update (only if automatic doesn't work for some obscure reason)
|
||||
|
||||
#### Batocera/Knulli/Retrobat
|
||||
1. Download latest update : https://github.com/RetroGameSets/RGSX/releases/latest/download/RGSX_update_latest.zip
|
||||
2. Extract zip content in `/userdata/roms/ports/RGSX`
|
||||
3. Launch RGSX
|
||||
|
||||
|
||||
### 📖 Documentation
|
||||
[README.md](https://github.com/${{ github.repository }}/blob/main/README.md)
|
||||
|
||||
## SUPPORT US
|
||||
Donate , Buy me a beer or a coffee :
|
||||
if you want to support my project you can look here 🙂 https://bit.ly/donate-to-rgsx
|
||||
|
||||
Affiliate links :
|
||||
hi all if you want to buy a premium account, you can use affiliated links here to support dev of RGSX without donate anything :
|
||||
DEBRID-LINK.FR : https://debrid-link.fr/id/ow1DD
|
||||
1FICHIER.COM : https://1fichier.com/?af=3186111
|
||||
REAL-DEBRID.FR : http://real-debrid.com/?id=8441
|
||||
RELEASE_EOF
|
||||
|
||||
echo "✓ Release notes generated"
|
||||
cat dist/RELEASE_NOTES.md
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: RGSX ${{ github.ref_name }}
|
||||
generate_release_notes: true
|
||||
generate_release_notes: false
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: |
|
||||
# 📦 RGSX Release ${{ github.ref_name }}
|
||||
|
||||
## 📥 Automatic Installation (Only for batocera Knulli)
|
||||
|
||||
### ON PC , NUC, SteamDeck or any x86_64 based:
|
||||
1. Open File Manager (F1) then "Applications" and launch xterm
|
||||
2. Use the command line `curl -L bit.ly/rgsx-install | sh`
|
||||
3. Launch RGSX from "Ports" menu
|
||||
|
||||
### ON RASPBERRY/ ARM based SBC / HANDHELD :
|
||||
1. Connect your device with SSH on a computer/smartphone connected to same network (ssh root@IPADDRESS , pass:linux)
|
||||
2. Use the command line `curl -L bit.ly/rgsx-install | sh`
|
||||
3. Launch RGSX from "Ports" menu
|
||||
|
||||
## 📥 Manual Installation
|
||||
|
||||
### Batocera/Knulli
|
||||
1. Download latest release : https://github.com/RetroGameSets/RGSX/releases/latest/download/RGSX_full_latest.zip
|
||||
2. Extract only PORTS folder in `/userdata/roms/`
|
||||
3. Launch RGSX from the Ports menu
|
||||
|
||||
### Retrobat/Full Installation on Windows
|
||||
1. Download latest release : https://github.com/RetroGameSets/RGSX/releases/latest/download/RGSX_full_latest.zip
|
||||
2. Extract all folders in your Retrobat\roms folder
|
||||
3. Launch RGSX from system "Windows"
|
||||
|
||||
|
||||
## 📥 Manual Update (only if automatic doesn't work for some obcure reason)
|
||||
|
||||
#### Batocera/Knulli/Retrobat
|
||||
1. Download latest update : https://github.com/RetroGameSets/RGSX/releases/latest/download/RGSX_update_latest.zip
|
||||
2. Extract zip content on in `/userdata/roms/ports/RGSX`
|
||||
3. Launch RGSX
|
||||
|
||||
|
||||
### 📖 Documentation
|
||||
[README.md](https://github.com/${{ github.repository }}/blob/main/README.md)
|
||||
body_path: dist/RELEASE_NOTES.md
|
||||
files: |
|
||||
dist/RGSX_update_latest.zip
|
||||
dist/RGSX_full_latest.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Send changelog to Discord
|
||||
run: |
|
||||
CHANGELOG=$(git log -1 --format=%B ${{ github.ref_name }} | sed ':a;N;$!ba;s/\n/\\n/g')
|
||||
|
||||
curl -H "Content-Type: application/json" \
|
||||
-X POST \
|
||||
-d "{
|
||||
\"username\": \"RGSX Releases Bot\",
|
||||
\"avatar_url\": \"https://retrogamesets.fr/assets/images/avatar.png\",
|
||||
\"content\": \"📦 **RGSX ${{ github.ref_name }}**\n\n📝 **Changelog :**\n${CHANGELOG}\"
|
||||
}" \
|
||||
${{ secrets.DISCORD_WEBHOOK }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,3 +23,4 @@ pygame/
|
||||
data/
|
||||
docker-compose.test.yml
|
||||
config/
|
||||
pyrightconfig.json
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
A free, user-friendly ROM downloader for Batocera, Knulli, and RetroBat with multi-source support.
|
||||
|
||||
<p align="center">
|
||||
<img width="69%" alt="platform menu" src="https://github.com/user-attachments/assets/4464b57b-06a8-45e9-a411-cc12b421545a" />
|
||||
<img width="69%" alt="main" src="https://github.com/user-attachments/assets/a98f1189-9a50-4cc3-b588-3f85245640d8" />
|
||||
<img width="30%" alt="controls help" src="https://github.com/user-attachments/assets/38cac7e6-14f2-4e83-91da-0679669822ee" />
|
||||
</p>
|
||||
<p align="center">
|
||||
|
||||
@@ -28,13 +28,14 @@ from display import (
|
||||
init_display, draw_loading_screen, draw_error_screen, draw_platform_grid,
|
||||
draw_progress_screen, draw_controls, draw_virtual_keyboard,
|
||||
draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list,
|
||||
draw_global_search_list,
|
||||
draw_display_menu, draw_filter_menu_choice, draw_filter_advanced, draw_filter_priority_config,
|
||||
draw_history_list, draw_clear_history_dialog, draw_cancel_download_dialog,
|
||||
draw_confirm_dialog, draw_reload_games_data_dialog, draw_popup, draw_gradient,
|
||||
draw_toast, show_toast, THEME_COLORS
|
||||
draw_toast, show_toast, THEME_COLORS, sync_display_metrics
|
||||
)
|
||||
from language import _
|
||||
from network import test_internet, download_rom, is_1fichier_url, download_from_1fichier, check_for_updates, cancel_all_downloads, download_queue_worker
|
||||
from network import test_internet, download_rom, is_1fichier_url, download_from_1fichier, check_for_updates, apply_pending_update, cancel_all_downloads, download_queue_worker
|
||||
from controls import handle_controls, validate_menu_state, process_key_repeats, get_emergency_controls
|
||||
from controls_mapper import map_controls, draw_controls_mapping, get_actions
|
||||
from controls import load_controls_config
|
||||
@@ -44,7 +45,7 @@ from utils import (
|
||||
)
|
||||
from history import load_history, save_history, load_downloaded_games
|
||||
from config import OTA_data_ZIP
|
||||
from rgsx_settings import get_sources_mode, get_custom_sources_url, get_sources_zip_url
|
||||
from rgsx_settings import get_sources_mode, get_custom_sources_url, get_sources_zip_url, get_display_fullscreen
|
||||
from accessibility import load_accessibility_settings
|
||||
|
||||
# Configuration du logging
|
||||
@@ -64,7 +65,7 @@ except Exception as e:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Ensure API key files (1Fichier, AllDebrid, RealDebrid) exist at startup so user can fill them before any download
|
||||
# Ensure API key files (1Fichier, AllDebrid, Debrid-Link, RealDebrid) exist at startup so user can fill them before any download
|
||||
try: # pragma: no cover
|
||||
load_api_keys(False)
|
||||
except Exception as _e:
|
||||
@@ -97,6 +98,10 @@ _run_windows_gamelist_update()
|
||||
|
||||
try:
|
||||
config.update_checked = False
|
||||
config.gamelist_update_prompted = False # Flag pour ne pas redemander la mise à jour plusieurs fois
|
||||
config.pending_update_version = ""
|
||||
config.startup_update_confirmed = False
|
||||
config.text_file_mode = ""
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -146,6 +151,14 @@ initialize_language()
|
||||
config.sources_mode = get_sources_mode()
|
||||
config.custom_sources_url = get_custom_sources_url()
|
||||
logger.debug(f"Mode sources initial: {config.sources_mode}, URL custom: {config.custom_sources_url}")
|
||||
# Charger l'option nintendo_layout depuis les settings
|
||||
try:
|
||||
from rgsx_settings import get_nintendo_layout
|
||||
config.nintendo_layout = get_nintendo_layout()
|
||||
logger.debug(f"nintendo_layout initial: {config.nintendo_layout}")
|
||||
except Exception:
|
||||
# fallback: si l'import ou la lecture échoue, conserver la valeur par défaut dans config
|
||||
logger.debug("Impossible de charger nintendo_layout depuis rgsx_settings")
|
||||
|
||||
# Détection du système grace a une commande windows / linux (on oublie is non-pc c'est juste pour connaitre le materiel et le systeme d'exploitation)
|
||||
def detect_system_info():
|
||||
@@ -176,7 +189,6 @@ config.init_footer_font()
|
||||
|
||||
# Mise à jour de la résolution dans config
|
||||
config.screen_width, config.screen_height = pygame.display.get_surface().get_size()
|
||||
logger.debug(f"Resolution d'ecran : {config.screen_width}x{config.screen_height}")
|
||||
print(f"Resolution ecran validee: {config.screen_width}x{config.screen_height}")
|
||||
|
||||
# Afficher un premier écran de chargement immédiatement pour éviter un écran noir
|
||||
@@ -276,7 +288,6 @@ logger.debug(f"Historique de téléchargement : {len(config.history)} entrées")
|
||||
|
||||
# Chargement des jeux téléchargés
|
||||
config.downloaded_games = load_downloaded_games()
|
||||
logger.debug(f"Jeux téléchargés : {sum(len(v) for v in config.downloaded_games.values())} jeux")
|
||||
|
||||
# Vérification et chargement de la configuration des contrôles (après mises à jour et détection manette)
|
||||
config.controls_config = load_controls_config()
|
||||
@@ -302,6 +313,9 @@ try:
|
||||
if config.controls_config:
|
||||
summary = {}
|
||||
for action, mapping in config.controls_config.items():
|
||||
# Vérifier que mapping est bien un dictionnaire
|
||||
if not isinstance(mapping, dict):
|
||||
continue
|
||||
mtype = mapping.get("type")
|
||||
val = None
|
||||
if mtype == "key":
|
||||
@@ -341,9 +355,6 @@ def start_web_server():
|
||||
global web_server_process
|
||||
try:
|
||||
web_server_script = os.path.join(config.APP_FOLDER, "rgsx_web.py")
|
||||
logger.info(f"Tentative de démarrage du serveur web...")
|
||||
logger.info(f"Script: {web_server_script}")
|
||||
logger.info(f"Fichier existe: {os.path.exists(web_server_script)}")
|
||||
|
||||
if not os.path.exists(web_server_script):
|
||||
logger.warning(f"Script serveur web introuvable: {web_server_script}")
|
||||
@@ -384,7 +395,6 @@ def start_web_server():
|
||||
|
||||
logger.info(f"✅ Serveur web démarré (PID: {web_server_process.pid})")
|
||||
logger.info(f"🌐 Serveur accessible sur http://localhost:5000")
|
||||
logger.info(f"📝 Logs de démarrage: {web_server_log}")
|
||||
|
||||
# Attendre un peu pour voir si le processus crash immédiatement
|
||||
import time
|
||||
@@ -423,7 +433,7 @@ def stop_web_server():
|
||||
|
||||
# Boucle principale
|
||||
async def main():
|
||||
global current_music, music_files, music_folder, joystick
|
||||
global current_music, music_files, music_folder, joystick, screen
|
||||
logger.debug("Début main")
|
||||
|
||||
# Charger les filtres de jeux sauvegardés
|
||||
@@ -447,10 +457,10 @@ async def main():
|
||||
# Démarrer le worker de la queue de téléchargement
|
||||
queue_worker_thread = threading.Thread(target=download_queue_worker, daemon=True)
|
||||
queue_worker_thread.start()
|
||||
logger.info("Worker de la queue de téléchargement démarré")
|
||||
|
||||
running = True
|
||||
loading_step = "none"
|
||||
ota_update_task = None
|
||||
sources = []
|
||||
config.last_state_change_time = 0
|
||||
config.debounce_delay = 50
|
||||
@@ -483,6 +493,9 @@ async def main():
|
||||
if config.menu_state == "download_progress" and current_time - last_redraw_time >= 100:
|
||||
config.needs_redraw = True
|
||||
last_redraw_time = current_time
|
||||
if config.menu_state == "loading" and current_time - last_redraw_time >= 100:
|
||||
config.needs_redraw = True
|
||||
last_redraw_time = current_time
|
||||
# Forcer redraw toutes les 100 ms dans history avec téléchargement actif
|
||||
if config.menu_state == "history" and any(entry["status"] in ["Downloading", "Téléchargement"] for entry in config.history):
|
||||
if current_time - last_redraw_time >= 100:
|
||||
@@ -521,7 +534,7 @@ async def main():
|
||||
# Appui long détecté, ouvrir le scraper
|
||||
games = config.filtered_games if config.filter_active or config.search_mode else config.games
|
||||
if games:
|
||||
game_name = games[config.current_game][0]
|
||||
game_name = games[config.current_game].name
|
||||
platform = config.platforms[config.current_platform]["name"] if isinstance(config.platforms[config.current_platform], dict) else config.platforms[config.current_platform]
|
||||
|
||||
config.previous_menu_state = "game"
|
||||
@@ -578,6 +591,24 @@ async def main():
|
||||
thread = threading.Thread(target=scrape_async, daemon=True)
|
||||
thread.start()
|
||||
|
||||
# Gestion de l'appui long sur confirm dans le menu platform pour configurer le dossier de destination
|
||||
if (config.menu_state == "platform" and
|
||||
getattr(config, 'platform_confirm_press_start_time', 0) > 0 and
|
||||
not getattr(config, 'platform_confirm_long_press_triggered', False)):
|
||||
press_duration = current_time - config.platform_confirm_press_start_time
|
||||
if press_duration >= config.confirm_long_press_threshold:
|
||||
# Appui long détecté, ouvrir le dialogue de configuration du dossier
|
||||
if config.platforms:
|
||||
platform = config.platforms[config.selected_platform]
|
||||
platform_name = platform["name"] if isinstance(platform, dict) else platform
|
||||
config.platform_config_name = platform_name
|
||||
config.previous_menu_state = "platform"
|
||||
config.menu_state = "platform_folder_config"
|
||||
config.platform_folder_selection = 0 # 0=Current, 1=Browse, 2=Reset, 3=Cancel
|
||||
config.needs_redraw = True
|
||||
config.platform_confirm_long_press_triggered = True
|
||||
logger.debug(f"Appui long détecté ({press_duration}ms), ouverture config dossier pour {platform_name}")
|
||||
|
||||
# Gestion des événements
|
||||
events = pygame.event.get()
|
||||
for event in events:
|
||||
@@ -586,6 +617,27 @@ async def main():
|
||||
current_music = play_random_music(music_files, music_folder, current_music)
|
||||
continue
|
||||
|
||||
resize_events = {
|
||||
getattr(pygame, 'VIDEORESIZE', -1),
|
||||
getattr(pygame, 'WINDOWSIZECHANGED', -2),
|
||||
getattr(pygame, 'WINDOWRESIZED', -3),
|
||||
}
|
||||
if event.type in resize_events and not get_display_fullscreen():
|
||||
try:
|
||||
if event.type == getattr(pygame, 'VIDEORESIZE', -1):
|
||||
new_width = max(640, int(getattr(event, 'w', config.screen_width)))
|
||||
new_height = max(360, int(getattr(event, 'h', config.screen_height)))
|
||||
screen = pygame.display.set_mode((new_width, new_height), pygame.RESIZABLE)
|
||||
else:
|
||||
screen = pygame.display.get_surface() or screen
|
||||
|
||||
sync_display_metrics(screen)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Fenêtre redimensionnée: {config.screen_width}x{config.screen_height}")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors du redimensionnement de la fenêtre: {e}")
|
||||
continue
|
||||
|
||||
if event.type == pygame.QUIT:
|
||||
config.menu_state = "confirm_exit"
|
||||
config.confirm_selection = 0
|
||||
@@ -638,6 +690,7 @@ async def main():
|
||||
# Basculer sur les contrôles clavier
|
||||
config.joystick = False
|
||||
config.keyboard = True
|
||||
config.controller_device_name = ""
|
||||
# Recharger la configuration des contrôles pour le clavier
|
||||
config.controls_config = load_controls_config()
|
||||
logger.info("Contrôles clavier chargés")
|
||||
@@ -646,6 +699,7 @@ async def main():
|
||||
# Basculer sur les contrôles clavier
|
||||
config.joystick = False
|
||||
config.keyboard = True
|
||||
config.controller_device_name = ""
|
||||
# Recharger la configuration des contrôles pour le clavier
|
||||
config.controls_config = load_controls_config()
|
||||
logger.info("Contrôles clavier chargés")
|
||||
@@ -681,9 +735,12 @@ async def main():
|
||||
"pause_menu",
|
||||
"pause_controls_menu",
|
||||
"pause_display_menu",
|
||||
"pause_display_layout_menu",
|
||||
"pause_display_font_menu",
|
||||
"pause_games_menu",
|
||||
"pause_settings_menu",
|
||||
"pause_api_keys_status",
|
||||
"pause_connection_status",
|
||||
"filter_platforms",
|
||||
"display_menu",
|
||||
"language_select",
|
||||
@@ -694,15 +751,16 @@ async def main():
|
||||
"history_game_options",
|
||||
"history_show_folder",
|
||||
"history_scraper_info",
|
||||
"scraper", # Ajout du scraper pour gérer les contrôles
|
||||
"scraper",
|
||||
"history_error_details",
|
||||
"history_confirm_delete",
|
||||
"history_extract_archive",
|
||||
"text_file_viewer", # Visualiseur de fichiers texte
|
||||
"text_file_viewer",
|
||||
# Menus filtrage avancé
|
||||
"filter_menu_choice",
|
||||
"filter_advanced",
|
||||
"filter_priority_config",
|
||||
"platform_search",
|
||||
}
|
||||
if config.menu_state in SIMPLE_HANDLE_STATES:
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
@@ -737,6 +795,26 @@ async def main():
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
|
||||
if config.menu_state == "gamelist_update_prompt":
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
|
||||
if config.menu_state == "platform_folder_config":
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
|
||||
if config.menu_state == "folder_browser":
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
|
||||
if config.menu_state == "folder_browser_new_folder":
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
|
||||
if config.menu_state == "extension_warning":
|
||||
logger.debug(f"[EXTENSION_WARNING] Processing extension_warning, previous_menu_state={config.previous_menu_state}, pending_download={bool(config.pending_download)}")
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
@@ -789,12 +867,9 @@ async def main():
|
||||
logger.debug("Action quit détectée, arrêt de l'application")
|
||||
elif action == "download" and config.menu_state == "game" and config.filtered_games:
|
||||
game = config.filtered_games[config.current_game]
|
||||
if isinstance(game, (list, tuple)):
|
||||
game_name = game[0]
|
||||
url = game[1] if len(game) > 1 else None
|
||||
else: # fallback str
|
||||
game_name = str(game)
|
||||
url = None
|
||||
game_name = game.name
|
||||
url = game.url
|
||||
|
||||
# Nouveau schéma: config.platforms contient déjà platform_name (string)
|
||||
platform_name = config.platforms[config.current_platform]
|
||||
if url:
|
||||
@@ -806,7 +881,12 @@ async def main():
|
||||
keys_info = ensure_download_provider_keys(False)
|
||||
except Exception as e:
|
||||
logger.error(f"Impossible de charger les clés via helpers: {e}")
|
||||
keys_info = {'1fichier': getattr(config,'API_KEY_1FICHIER',''), 'alldebrid': getattr(config,'API_KEY_ALLDEBRID',''), 'realdebrid': getattr(config,'API_KEY_REALDEBRID','')}
|
||||
keys_info = {
|
||||
'1fichier': getattr(config,'API_KEY_1FICHIER',''),
|
||||
'alldebrid': getattr(config,'API_KEY_ALLDEBRID',''),
|
||||
'debridlink': getattr(config,'API_KEY_DEBRIDLINK',''),
|
||||
'realdebrid': getattr(config,'API_KEY_REALDEBRID','')
|
||||
}
|
||||
|
||||
# SUPPRIMÉ: Vérification clés API obligatoires
|
||||
# Maintenant on a le mode gratuit en fallback automatique
|
||||
@@ -970,7 +1050,8 @@ async def main():
|
||||
if success:
|
||||
toast_msg = f"[OK] {game_name}\n{_('download_completed') if _ else 'Download completed'}"
|
||||
else:
|
||||
toast_msg = f"[ERROR] {game_name}\n{_('download_failed') if _ else 'Download failed'}"
|
||||
toast_body = message or (_('download_failed') if _ else 'Download failed')
|
||||
toast_msg = f"[ERROR] {game_name}\n{toast_body}"
|
||||
show_toast(toast_msg, 3000)
|
||||
config.needs_redraw = True
|
||||
del config.download_tasks[task_id]
|
||||
@@ -992,7 +1073,8 @@ async def main():
|
||||
config.download_progress.clear()
|
||||
config.pending_download = None
|
||||
# Afficher un toast au lieu de changer de page
|
||||
toast_msg = f"[ERROR] {game_name}\n{_('download_failed') if _ else 'Download failed'}"
|
||||
toast_body = message or (_('download_failed') if _ else 'Download failed')
|
||||
toast_msg = f"[ERROR] {game_name}\n{toast_body}"
|
||||
show_toast(toast_msg, 3000)
|
||||
config.needs_redraw = True
|
||||
del config.download_tasks[task_id]
|
||||
@@ -1031,7 +1113,8 @@ async def main():
|
||||
if success:
|
||||
toast_msg = f"[OK] {game_name}\n{_('download_completed') if _ else 'Download completed'}"
|
||||
else:
|
||||
toast_msg = f"[ERROR] {game_name}\n{_('download_failed') if _ else 'Download failed'}"
|
||||
toast_body = message or (_('download_failed') if _ else 'Download failed')
|
||||
toast_msg = f"[ERROR] {game_name}\n{toast_body}"
|
||||
show_toast(toast_msg, 3000)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"[DOWNLOAD_TASK] Toast displayed after completion, task_id={task_id}")
|
||||
@@ -1073,6 +1156,10 @@ async def main():
|
||||
draw_game_list(screen)
|
||||
if getattr(config, 'joystick', False):
|
||||
draw_virtual_keyboard(screen)
|
||||
elif config.menu_state == "platform_search":
|
||||
draw_global_search_list(screen)
|
||||
if getattr(config, 'joystick', False) and getattr(config, 'global_search_editing', False):
|
||||
draw_virtual_keyboard(screen)
|
||||
elif config.menu_state == "download_progress":
|
||||
draw_progress_screen(screen)
|
||||
# État download_result supprimé
|
||||
@@ -1089,6 +1176,12 @@ async def main():
|
||||
elif config.menu_state == "pause_display_menu":
|
||||
from display import draw_pause_display_menu
|
||||
draw_pause_display_menu(screen, getattr(config, 'pause_display_selection', 0))
|
||||
elif config.menu_state == "pause_display_layout_menu":
|
||||
from display import draw_pause_display_layout_menu
|
||||
draw_pause_display_layout_menu(screen, getattr(config, 'pause_display_layout_selection', 0))
|
||||
elif config.menu_state == "pause_display_font_menu":
|
||||
from display import draw_pause_display_font_menu
|
||||
draw_pause_display_font_menu(screen, getattr(config, 'pause_display_font_selection', 0))
|
||||
elif config.menu_state == "pause_games_menu":
|
||||
from display import draw_pause_games_menu
|
||||
draw_pause_games_menu(screen, getattr(config, 'pause_games_selection', 0))
|
||||
@@ -1098,6 +1191,9 @@ async def main():
|
||||
elif config.menu_state == "pause_api_keys_status":
|
||||
from display import draw_pause_api_keys_status
|
||||
draw_pause_api_keys_status(screen)
|
||||
elif config.menu_state == "pause_connection_status":
|
||||
from display import draw_pause_connection_status
|
||||
draw_pause_connection_status(screen)
|
||||
elif config.menu_state == "filter_platforms":
|
||||
from display import draw_filter_platforms_menu
|
||||
draw_filter_platforms_menu(screen)
|
||||
@@ -1145,6 +1241,18 @@ async def main():
|
||||
draw_cancel_download_dialog(screen)
|
||||
elif config.menu_state == "reload_games_data":
|
||||
draw_reload_games_data_dialog(screen)
|
||||
elif config.menu_state == "gamelist_update_prompt":
|
||||
from display import draw_gamelist_update_prompt
|
||||
draw_gamelist_update_prompt(screen)
|
||||
elif config.menu_state == "platform_folder_config":
|
||||
from display import draw_platform_folder_config_dialog
|
||||
draw_platform_folder_config_dialog(screen)
|
||||
elif config.menu_state == "folder_browser":
|
||||
from display import draw_folder_browser
|
||||
draw_folder_browser(screen)
|
||||
elif config.menu_state == "folder_browser_new_folder":
|
||||
from display import draw_folder_browser_new_folder
|
||||
draw_folder_browser_new_folder(screen)
|
||||
elif config.menu_state == "restart_popup":
|
||||
draw_popup(screen)
|
||||
elif config.menu_state == "accessibility_menu":
|
||||
@@ -1235,6 +1343,7 @@ async def main():
|
||||
config.loading_progress = 20.0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
|
||||
continue # Passer immédiatement à check_ota
|
||||
else:
|
||||
config.menu_state = "error"
|
||||
config.error_message = _("error_no_internet")
|
||||
@@ -1258,12 +1367,49 @@ async def main():
|
||||
config.error_message = message or _("error_check_updates_failed")
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Erreur OTA : {message}")
|
||||
elif getattr(config, "pending_update_version", ""):
|
||||
loading_step = "await_ota_confirmation"
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
else:
|
||||
loading_step = "check_data"
|
||||
config.current_loading_system = _("loading_downloading_games_images")
|
||||
config.loading_progress = 50.0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
|
||||
continue # Passer immédiatement à check_data
|
||||
elif loading_step == "await_ota_confirmation":
|
||||
if not getattr(config, "startup_update_confirmed", False):
|
||||
await asyncio.sleep(0.01)
|
||||
continue
|
||||
|
||||
latest_version = getattr(config, "pending_update_version", "")
|
||||
config.startup_update_confirmed = False
|
||||
ota_update_task = asyncio.create_task(apply_pending_update(latest_version))
|
||||
loading_step = "apply_ota_update"
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
elif loading_step == "apply_ota_update":
|
||||
if ota_update_task is None:
|
||||
loading_step = "check_data"
|
||||
continue
|
||||
if not ota_update_task.done():
|
||||
await asyncio.sleep(0.01)
|
||||
continue
|
||||
|
||||
success, message = await ota_update_task
|
||||
ota_update_task = None
|
||||
if not success:
|
||||
config.menu_state = "error"
|
||||
config.error_message = message or _("error_check_updates_failed")
|
||||
config.needs_redraw = True
|
||||
else:
|
||||
config.pending_update_version = ""
|
||||
config.text_file_mode = ""
|
||||
config.text_file_content = ""
|
||||
config.loading_detail_lines = []
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
elif loading_step == "check_data":
|
||||
is_data_empty = not os.path.exists(config.GAMES_FOLDER) or not any(os.scandir(config.GAMES_FOLDER))
|
||||
if is_data_empty:
|
||||
@@ -1271,6 +1417,7 @@ async def main():
|
||||
config.loading_progress = 30.0
|
||||
config.needs_redraw = True
|
||||
logger.debug("Dossier Data vide, début du téléchargement du ZIP")
|
||||
sources_zip_url = None # Initialiser pour éviter les erreurs
|
||||
try:
|
||||
zip_path = os.path.join(config.SAVE_FOLDER, "data_download.zip")
|
||||
headers = {'User-Agent': 'Mozilla/5.0'}
|
||||
@@ -1376,13 +1523,51 @@ async def main():
|
||||
config.loading_progress = 80.0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Dossier Data non vide, passage à {loading_step}")
|
||||
continue # Passer immédiatement à load_sources
|
||||
elif loading_step == "load_sources":
|
||||
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
|
||||
sources = load_sources()
|
||||
config.menu_state = "platform"
|
||||
config.loading_progress = 100.0
|
||||
config.current_loading_system = ""
|
||||
|
||||
# Vérifier si une mise à jour de la liste des jeux est nécessaire (seulement si pas déjà demandé)
|
||||
if not config.gamelist_update_prompted:
|
||||
from rgsx_settings import get_last_gamelist_update
|
||||
from config import GAMELIST_UPDATE_DAYS
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
last_update = get_last_gamelist_update()
|
||||
should_prompt_update = False
|
||||
|
||||
if last_update is None:
|
||||
# Première utilisation, proposer la mise à jour
|
||||
logger.info("Première utilisation détectée, proposition de mise à jour de la liste des jeux")
|
||||
should_prompt_update = True
|
||||
else:
|
||||
try:
|
||||
last_update_date = datetime.strptime(last_update, "%Y-%m-%d")
|
||||
days_since_update = (datetime.now() - last_update_date).days
|
||||
logger.info(f"Dernière mise à jour de la liste des jeux: {last_update} ({days_since_update} jours)")
|
||||
|
||||
if days_since_update >= GAMELIST_UPDATE_DAYS:
|
||||
logger.info(f"Mise à jour de la liste des jeux recommandée (>{GAMELIST_UPDATE_DAYS} jours)")
|
||||
should_prompt_update = True
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la vérification de la date de mise à jour: {e}")
|
||||
|
||||
if should_prompt_update:
|
||||
config.menu_state = "gamelist_update_prompt"
|
||||
config.gamelist_update_selection = 1 # 0=Non, 1=Oui (par défaut)
|
||||
config.gamelist_update_prompted = True # Marquer comme déjà demandé
|
||||
logger.debug("Affichage du prompt de mise à jour de la liste des jeux")
|
||||
else:
|
||||
config.menu_state = "platform"
|
||||
logger.debug(f"Fin chargement, passage à platform, progress={config.loading_progress}")
|
||||
else:
|
||||
config.menu_state = "platform"
|
||||
logger.debug(f"Prompt déjà affiché, passage à platform, progress={config.loading_progress}")
|
||||
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Fin chargement, passage à platform, progress={config.loading_progress}")
|
||||
|
||||
# Gestion de l'état de transition
|
||||
if config.transition_state == "to_game":
|
||||
|
||||
1
ports/RGSX/assets/ArchiveOrgCookie.txt
Normal file
1
ports/RGSX/assets/ArchiveOrgCookie.txt
Normal file
@@ -0,0 +1 @@
|
||||
donation-identifier=39546f3b2d3f67a664818596d81a5bec; abtest-identifier=fee0e28eb6c8d0de147d19db4303ee84; logged-in-sig=1802098179%201770562179%20AKHN8aF4EsFeR%2FundhgQTu0j27ZdFZXmgyUiqnJvXq%2BwtDGVvapqhKUFhIlI9bXAMYLMHDRJoO76bsqXI662nrIsx58efihNrafdk285r8MAdotWx03usO30baYoNPoMMEaK8iuhtbfTEyfE7oTZwdO7wjxNUTm%2Bbjjm6kmUD3HSQRzPsc0oWrrnd8Wj2x3UiuZeRnBfC60OjJHcnKC2Xv7teS%2BBx3EdKAG1i739MxTzjtEfERWw83bnaV30827qaFhZ%2BDK3%2FwCGOUwtablPA%2B0EeLR9%2BoYeC6x5aaJMZHBMjBowSIEE4QAK9IG9haBsn7%2F1PCweYuLivMIZJeA7mA%3D%3D; logged-in-user=rgsx%40outlook.fr
|
||||
@@ -7,7 +7,7 @@
|
||||
"cancel": {
|
||||
"type": "key",
|
||||
"key": 27,
|
||||
"display": "\u00c9chap"
|
||||
"display": "Esc/Echap"
|
||||
},
|
||||
"up": {
|
||||
"type": "key",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ports/RGSX/assets/music/arcade-beat.mp3
Normal file
BIN
ports/RGSX/assets/music/arcade-beat.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/music/arthurhale.mp3
Normal file
BIN
ports/RGSX/assets/music/arthurhale.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/music/battle-time.mp3
Normal file
BIN
ports/RGSX/assets/music/battle-time.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/music/dream-in-keygen.mp3
Normal file
BIN
ports/RGSX/assets/music/dream-in-keygen.mp3
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ports/RGSX/assets/music/happy-videogame.mp3
Normal file
BIN
ports/RGSX/assets/music/happy-videogame.mp3
Normal file
Binary file not shown.
Binary file not shown.
BIN
ports/RGSX/assets/music/man-is-he-mega.mp3
Normal file
BIN
ports/RGSX/assets/music/man-is-he-mega.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/music/moodmode-8-bit.mp3
Normal file
BIN
ports/RGSX/assets/music/moodmode-8-bit.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/music/niknet_art.mp3
Normal file
BIN
ports/RGSX/assets/music/niknet_art.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/music/pixel-dreams.mp3
Normal file
BIN
ports/RGSX/assets/music/pixel-dreams.mp3
Normal file
Binary file not shown.
Binary file not shown.
BIN
ports/RGSX/assets/music/pixelated-dreams.mp3
Normal file
BIN
ports/RGSX/assets/music/pixelated-dreams.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/music/reflextunes-game-zone.mp3
Normal file
BIN
ports/RGSX/assets/music/reflextunes-game-zone.mp3
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ports/RGSX/assets/music/video-game-short.mp3
Normal file
BIN
ports/RGSX/assets/music/video-game-short.mp3
Normal file
Binary file not shown.
BIN
ports/RGSX/assets/progs/aria2c.exe
Normal file
BIN
ports/RGSX/assets/progs/aria2c.exe
Normal file
Binary file not shown.
69
ports/RGSX/assets/progs/versionclean
Normal file
69
ports/RGSX/assets/progs/versionclean
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# BATOCERA SERVICE
|
||||
# name: Version Clean Service
|
||||
# description: Clean batocera-version output (hide extra services)
|
||||
# author: batocera-unofficial-addons
|
||||
# depends:
|
||||
# version: 1.0
|
||||
|
||||
SERVICE_NAME="versionclean"
|
||||
TARGET="/usr/bin/batocera-version"
|
||||
BACKUP="${TARGET}.bak"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
# If we've already backed up, assume it's already "cleaned"
|
||||
if [ -f "$BACKUP" ]; then
|
||||
echo "${SERVICE_NAME}: already started (backup exists at ${BACKUP})."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "${SERVICE_NAME}: backing up original ${TARGET} to ${BACKUP}..."
|
||||
cp "$TARGET" "$BACKUP"
|
||||
|
||||
echo "${SERVICE_NAME}: writing clean version script to ${TARGET}..."
|
||||
cat << 'EOF' > "$TARGET"
|
||||
#!/bin/bash
|
||||
|
||||
# Clean batocera-version
|
||||
# - "batocera-version --extra" -> "none"
|
||||
# - "batocera-version" -> contents of /usr/share/batocera/batocera.version
|
||||
|
||||
if test "$1" = "--extra"; then
|
||||
echo "none"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat /usr/share/batocera/batocera.version
|
||||
EOF
|
||||
|
||||
chmod +x "$TARGET"
|
||||
echo "${SERVICE_NAME}: clean version applied."
|
||||
;;
|
||||
|
||||
stop)
|
||||
if [ -f "$BACKUP" ]; then
|
||||
echo "${SERVICE_NAME}: restoring original ${TARGET} from ${BACKUP}..."
|
||||
cp "$BACKUP" "$TARGET"
|
||||
rm "$BACKUP"
|
||||
echo "${SERVICE_NAME}: restore complete."
|
||||
else
|
||||
echo "${SERVICE_NAME}: no backup found, nothing to restore."
|
||||
fi
|
||||
;;
|
||||
|
||||
status)
|
||||
if [ -f "$BACKUP" ]; then
|
||||
echo "${SERVICE_NAME}: CLEAN VERSION ACTIVE (backup present)."
|
||||
else
|
||||
echo "${SERVICE_NAME}: ORIGINAL VERSION ACTIVE."
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -1,6 +1,20 @@
|
||||
|
||||
import os
|
||||
import logging
|
||||
import platform
|
||||
import socket
|
||||
from typing import Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Game:
|
||||
name: str
|
||||
url: Optional[str]
|
||||
size: Optional[str]
|
||||
display_name: str # name withou file extension or platform prefix
|
||||
regions: Optional[list[str]] = None
|
||||
is_non_release: Optional[bool] = None
|
||||
base_name: Optional[str] = None
|
||||
|
||||
# Headless mode for CLI: set env RGSX_HEADLESS=1 to avoid pygame and noisy prints
|
||||
HEADLESS = os.environ.get("RGSX_HEADLESS") == "1"
|
||||
@@ -13,7 +27,10 @@ except Exception:
|
||||
pygame = None # type: ignore
|
||||
|
||||
# Version actuelle de l'application
|
||||
app_version = "2.4.0.1"
|
||||
app_version = "2.6.1.6.1"
|
||||
|
||||
# Nombre de jours avant de proposer la mise à jour de la liste des jeux
|
||||
GAMELIST_UPDATE_DAYS = 1
|
||||
|
||||
|
||||
def get_application_root():
|
||||
@@ -137,6 +154,12 @@ pending_download_is_queue = False # Indique si pending_download doit être ajou
|
||||
# Indique si un téléchargement est en cours
|
||||
download_active = False
|
||||
|
||||
# Cache status de connexion (menu pause > settings)
|
||||
connection_status = {}
|
||||
connection_status_timestamp = 0.0
|
||||
connection_status_in_progress = False
|
||||
connection_status_progress = {"done": 0, "total": 0}
|
||||
|
||||
# Log directory
|
||||
# Docker mode: /config/logs (persisted in config volume)
|
||||
# Traditional mode: /app/RGSX/logs (current behavior)
|
||||
@@ -175,7 +198,9 @@ DOWNLOADED_GAMES_PATH = os.path.join(SAVE_FOLDER, "downloaded_games.json")
|
||||
RGSX_SETTINGS_PATH = os.path.join(SAVE_FOLDER, "rgsx_settings.json")
|
||||
API_KEY_1FICHIER_PATH = os.path.join(SAVE_FOLDER, "1FichierAPI.txt")
|
||||
API_KEY_ALLDEBRID_PATH = os.path.join(SAVE_FOLDER, "AllDebridAPI.txt")
|
||||
API_KEY_DEBRIDLINK_PATH = os.path.join(SAVE_FOLDER, "DebridLinkAPI.txt")
|
||||
API_KEY_REALDEBRID_PATH = os.path.join(SAVE_FOLDER, "RealDebridAPI.txt")
|
||||
ARCHIVE_ORG_COOKIE_PATH = os.path.join(APP_FOLDER, "assets", "ArchiveOrgCookie.txt")
|
||||
|
||||
|
||||
|
||||
@@ -200,6 +225,8 @@ PS3DEC_EXE = os.path.join(APP_FOLDER,"assets", "progs", "ps3dec_win.exe")
|
||||
PS3DEC_LINUX = os.path.join(APP_FOLDER,"assets", "progs", "ps3dec_linux")
|
||||
SEVEN_Z_LINUX = os.path.join(APP_FOLDER,"assets", "progs", "7zz")
|
||||
SEVEN_Z_EXE = os.path.join(APP_FOLDER,"assets", "progs", "7z.exe")
|
||||
ARIA2C_EXE = os.path.join(APP_FOLDER,"assets", "progs", "aria2c.exe")
|
||||
ARIA2C_LINUX = os.path.join(APP_FOLDER,"assets", "progs", "aria2c")
|
||||
|
||||
# Détection du système d'exploitation (une seule fois au démarrage)
|
||||
OPERATING_SYSTEM = platform.system()
|
||||
@@ -226,6 +253,30 @@ SYSTEM_INFO = {
|
||||
def get_batocera_system_info():
|
||||
"""Récupère les informations système via la commande batocera-info."""
|
||||
global SYSTEM_INFO
|
||||
|
||||
def get_local_network_ip():
|
||||
try:
|
||||
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
udp_socket.connect(("8.8.8.8", 80))
|
||||
local_ip = udp_socket.getsockname()[0]
|
||||
if local_ip and not local_ip.startswith("127."):
|
||||
return local_ip
|
||||
finally:
|
||||
udp_socket.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
hostname = socket.gethostname()
|
||||
local_ip = socket.gethostbyname(hostname)
|
||||
if local_ip and not local_ip.startswith("127."):
|
||||
return local_ip
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return ""
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run(['batocera-info'], capture_output=True, text=True, timeout=5)
|
||||
@@ -281,6 +332,7 @@ def get_batocera_system_info():
|
||||
SYSTEM_INFO["system"] = f"{platform.system()} {platform.release()}"
|
||||
SYSTEM_INFO["architecture"] = platform.machine()
|
||||
SYSTEM_INFO["cpu_model"] = platform.processor() or "Unknown"
|
||||
SYSTEM_INFO["network_ip"] = get_local_network_ip()
|
||||
|
||||
return False
|
||||
|
||||
@@ -343,6 +395,7 @@ platforms = [] # Liste des plateformes disponibles
|
||||
current_platform = 0 # Index de la plateforme actuelle sélectionnée
|
||||
platform_names = {} # {platform_id: platform_name}
|
||||
games_count = {} # Dictionnaire comptant le nombre de jeux par plateforme
|
||||
games_count_log_verbose = False # Log détaillé par fichier (sinon résumé compact)
|
||||
platform_dicts = [] # Liste des dictionnaires de plateformes
|
||||
|
||||
# Filtre plateformes
|
||||
@@ -352,7 +405,8 @@ filter_platforms_dirty = False # indique si modifications non sauvegardées
|
||||
filter_platforms_selection = [] # copie de travail des plateformes visibles (bool masque?) structure: list of (name, hidden_bool)
|
||||
|
||||
# Affichage des jeux et sélection
|
||||
games = [] # Liste des jeux pour la plateforme actuelle
|
||||
games: list[Game] = [] # Liste des jeux pour la plateforme actuelle
|
||||
fbneo_games = {}
|
||||
current_game = 0 # Index du jeu actuellement sélectionné
|
||||
menu_state = "loading" # État actuel de l'interface menu
|
||||
scroll_offset = 0 # Offset de défilement pour la liste des jeux
|
||||
@@ -375,11 +429,18 @@ sources_mode = "rgsx" # Mode des sources de jeux (rgsx/custom)
|
||||
custom_sources_url = {OTA_data_ZIP} # URL personnalisée si mode custom
|
||||
selected_language_index = 0 # Index de la langue sélectionnée dans la liste
|
||||
|
||||
|
||||
# Recherche et filtres
|
||||
filtered_games = [] # Liste des jeux filtrés par recherche ou filtre
|
||||
filtered_games: list[Game] = [] # Liste des jeux filtrés par recherche ou filtre
|
||||
search_mode = False # Indicateur si le mode recherche est actif
|
||||
search_query = "" # Chaîne de recherche saisie par l'utilisateur
|
||||
filter_active = False # Indicateur si un filtre est appliqué
|
||||
global_search_index = [] # Index de recherche global {platform, jeu} construit a l'ouverture
|
||||
global_search_results = [] # Resultats de la recherche globale inter-plateformes
|
||||
global_search_query = "" # Texte saisi pour la recherche globale
|
||||
global_search_selected = 0 # Index du resultat global selectionne
|
||||
global_search_scroll_offset = 0 # Offset de defilement des resultats globaux
|
||||
global_search_editing = False # True si le clavier virtuel est actif pour la recherche globale
|
||||
|
||||
# Variables pour le filtrage avancé
|
||||
selected_filter_choice = 0 # Index dans le menu de choix de filtrage (recherche / avancé)
|
||||
@@ -425,14 +486,20 @@ scraper_game_page_url = "" # URL de la page du jeu sur TheGamesDB
|
||||
# CLES API / PREMIUM HOSTS
|
||||
API_KEY_1FICHIER = ""
|
||||
API_KEY_ALLDEBRID = ""
|
||||
API_KEY_DEBRIDLINK = ""
|
||||
API_KEY_REALDEBRID = ""
|
||||
PREMIUM_HOST_MARKERS = [
|
||||
"1Fichier",
|
||||
"Debrid-Link",
|
||||
]
|
||||
hide_premium_systems = False # Indicateur pour masquer les systèmes premium
|
||||
|
||||
# Variables diverses
|
||||
update_checked = False
|
||||
pending_update_version = ""
|
||||
startup_update_confirmed = False
|
||||
text_file_mode = ""
|
||||
loading_detail_lines = []
|
||||
extension_confirm_selection = 0 # Index de sélection pour confirmation d'extension
|
||||
controls_config = {} # Configuration des contrôles personnalisés
|
||||
selected_key = (0, 0) # Position du curseur dans le clavier virtuel
|
||||
@@ -448,6 +515,22 @@ confirm_press_start_time = 0 # Timestamp du début de l'appui sur confirm
|
||||
confirm_long_press_threshold = 2000 # Durée en ms pour déclencher l'appui long (2 secondes)
|
||||
confirm_long_press_triggered = False # Flag pour éviter de déclencher plusieurs fois
|
||||
|
||||
# Détection d'appui long sur confirm (menu platform - pour config dossier)
|
||||
platform_confirm_press_start_time = 0 # Timestamp du début de l'appui sur confirm dans le menu platform
|
||||
platform_confirm_long_press_triggered = False # Flag pour éviter de déclencher plusieurs fois
|
||||
|
||||
# Configuration dossier personnalisé par plateforme
|
||||
platform_config_name = "" # Nom de la plateforme en cours de configuration
|
||||
platform_folder_selection = 0 # Index de sélection dans le menu de config dossier (0=Current, 1=Browse, 2=Reset, 3=Cancel)
|
||||
|
||||
# Navigateur de dossiers intégré (folder browser)
|
||||
folder_browser_path = "" # Chemin actuel dans le navigateur
|
||||
folder_browser_items = [] # Liste des éléments (dossiers) dans le répertoire actuel
|
||||
folder_browser_selection = 0 # Index de l'élément sélectionné
|
||||
folder_browser_scroll_offset = 0 # Offset de défilement
|
||||
folder_browser_visible_items = 10 # Nombre d'éléments visibles
|
||||
folder_browser_mode = "platform" # "platform" pour dossier plateforme, "roms_root" pour dossier ROMs principal
|
||||
|
||||
# Tenter la récupération de la famille de police sauvegardée
|
||||
try:
|
||||
from rgsx_settings import get_font_family # import tardif pour éviter dépendances circulaires lors de l'exécution initiale
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -69,7 +69,7 @@ SDL_TO_PYGAME_KEY = {
|
||||
# Noms lisibles pour les touches clavier
|
||||
KEY_NAMES = {
|
||||
pygame.K_RETURN: "Enter",
|
||||
pygame.K_ESCAPE: "Échap",
|
||||
pygame.K_ESCAPE: "Esc/Echap",
|
||||
pygame.K_SPACE: "Espace",
|
||||
pygame.K_UP: "↑",
|
||||
pygame.K_DOWN: "↓",
|
||||
@@ -87,7 +87,7 @@ KEY_NAMES = {
|
||||
pygame.K_RMETA: "RMeta",
|
||||
pygame.K_CAPSLOCK: "Verr Maj",
|
||||
pygame.K_NUMLOCK: "Verr Num",
|
||||
pygame.K_SCROLLOCK: "Verr Déf",
|
||||
pygame.K_SCROLLOCK: "Verr Def",
|
||||
pygame.K_a: "A",
|
||||
pygame.K_b: "B",
|
||||
pygame.K_c: "C",
|
||||
@@ -158,7 +158,7 @@ KEY_NAMES = {
|
||||
pygame.K_F15: "F15",
|
||||
pygame.K_INSERT: "Inser",
|
||||
pygame.K_DELETE: "Suppr",
|
||||
pygame.K_HOME: "Début",
|
||||
pygame.K_HOME: "Debut",
|
||||
pygame.K_END: "Fin",
|
||||
pygame.K_PAGEUP: "Page+",
|
||||
pygame.K_PAGEDOWN: "Page-",
|
||||
@@ -303,22 +303,43 @@ def _images_base_dir() -> str:
|
||||
|
||||
def _action_icon_filename(action_name: str) -> Optional[str]:
|
||||
# Map actions to icon filenames present in assets/images
|
||||
mapping = {
|
||||
"up": "dpad_up.svg",
|
||||
"down": "dpad_down.svg",
|
||||
"left": "dpad_left.svg",
|
||||
"right": "dpad_right.svg",
|
||||
"confirm": "buttons_south.svg", # A (south)
|
||||
"cancel": "buttons_east.svg", # B (east)
|
||||
"clear_history": "buttons_west.svg", # X (west)
|
||||
"history": "buttons_north.svg", # Y (north)
|
||||
"start": "button_start.svg",
|
||||
"filter": "button_select.svg",
|
||||
"delete": "button_l.svg", # LB
|
||||
"space": "button_r.svg", # RB
|
||||
"page_up": "button_lt.svg",
|
||||
"page_down": "button_rt.svg",
|
||||
}
|
||||
# Option d'inversion ABXY (A/B <-> X/Y) via config.nintendo_layout
|
||||
|
||||
is_nintendo = getattr(config, 'nintendo_layout', False)
|
||||
if is_nintendo:
|
||||
mapping = {
|
||||
"up": "dpad_up.svg",
|
||||
"down": "dpad_down.svg",
|
||||
"left": "dpad_left.svg",
|
||||
"right": "dpad_right.svg",
|
||||
"confirm": "buttons_east.svg",
|
||||
"cancel": "buttons_south.svg",
|
||||
"clear_history": "buttons_west.svg",
|
||||
"history": "buttons_north.svg",
|
||||
"start": "button_start.svg",
|
||||
"filter": "button_select.svg",
|
||||
"delete": "button_l.svg",
|
||||
"space": "button_r.svg",
|
||||
"page_up": "button_lt.svg",
|
||||
"page_down": "button_rt.svg",
|
||||
}
|
||||
else:
|
||||
mapping = {
|
||||
"up": "dpad_up.svg",
|
||||
"down": "dpad_down.svg",
|
||||
"left": "dpad_left.svg",
|
||||
"right": "dpad_right.svg",
|
||||
"confirm": "buttons_south.svg",
|
||||
"cancel": "buttons_east.svg",
|
||||
"clear_history": "buttons_north.svg",
|
||||
"history": "buttons_west.svg",
|
||||
"start": "button_start.svg",
|
||||
"filter": "button_select.svg",
|
||||
"delete": "button_l.svg",
|
||||
"space": "button_r.svg",
|
||||
"page_up": "button_lt.svg",
|
||||
"page_down": "button_rt.svg",
|
||||
}
|
||||
return mapping.get(action_name)
|
||||
|
||||
def _load_svg_icon_surface(svg_path: str, size: int) -> Optional[pygame.Surface]:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,8 @@ import re
|
||||
import logging
|
||||
from typing import List, Tuple, Dict, Any
|
||||
|
||||
from config import Game
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -156,9 +158,7 @@ class GameFilters:
|
||||
r'\([^\)]*PRERELEASE[^\)]*\)',
|
||||
r'\([^\)]*UNFINISHED[^\)]*\)',
|
||||
r'\([^\)]*WIP[^\)]*\)',
|
||||
r'\[[^\]]*BETA[^\]]*\]',
|
||||
r'\[[^\]]*DEMO[^\]]*\]',
|
||||
r'\[[^\]]*TEST[^\]]*\]'
|
||||
r'\([^\)]*BOOTLEG[^\)]*\)',
|
||||
]
|
||||
return any(re.search(pattern, name) for pattern in non_release_patterns)
|
||||
|
||||
@@ -191,11 +191,31 @@ class GameFilters:
|
||||
base = base + disc_info
|
||||
|
||||
return base
|
||||
|
||||
@staticmethod
|
||||
def get_cached_regions(game: Game) -> List[str]:
|
||||
"""Retourne les régions en les calculant une seule fois par jeu."""
|
||||
if game.regions is None:
|
||||
game.regions = GameFilters.get_game_regions(game.display_name)
|
||||
return game.regions
|
||||
|
||||
@staticmethod
|
||||
def get_cached_non_release(game: Game) -> bool:
|
||||
"""Retourne le flag non-release en le calculant à la demande."""
|
||||
if game.is_non_release is None:
|
||||
game.is_non_release = GameFilters.is_non_release_game(game.display_name)
|
||||
return game.is_non_release
|
||||
|
||||
@staticmethod
|
||||
def get_cached_base_name(game: Game) -> str:
|
||||
"""Retourne le nom de base en le calculant une seule fois par jeu."""
|
||||
if game.base_name is None:
|
||||
game.base_name = GameFilters.get_base_game_name(game.display_name)
|
||||
return game.base_name
|
||||
|
||||
def get_region_priority(self, game_name: str) -> int:
|
||||
def get_region_priority(self, game: Game) -> int:
|
||||
"""Obtient la priorité de région pour un jeu (pour one-rom-per-game)"""
|
||||
# Utiliser la fonction de détection de régions pour être cohérent
|
||||
game_regions = self.get_game_regions(game_name)
|
||||
game_regions = self.get_cached_regions(game)
|
||||
|
||||
# Trouver la meilleure priorité parmi toutes les régions détectées
|
||||
best_priority = len(self.region_priority) # Par défaut: priorité la plus basse
|
||||
@@ -211,7 +231,7 @@ class GameFilters:
|
||||
|
||||
return best_priority
|
||||
|
||||
def apply_filters(self, games: List[Tuple]) -> List[Tuple]:
|
||||
def apply_filters(self, games: list[Game]) -> list[Game]:
|
||||
"""
|
||||
Applique les filtres à une liste de jeux
|
||||
games: Liste de tuples (game_name, game_url, size)
|
||||
@@ -221,14 +241,13 @@ class GameFilters:
|
||||
return games
|
||||
|
||||
filtered_games = []
|
||||
has_region_excludes = any(state == 'exclude' for state in self.region_filters.values())
|
||||
|
||||
# Filtrage par région
|
||||
for game in games:
|
||||
game_name = game[0]
|
||||
|
||||
# Vérifier les filtres de région
|
||||
if self.region_filters:
|
||||
game_regions = self.get_game_regions(game_name)
|
||||
if has_region_excludes:
|
||||
game_regions = self.get_cached_regions(game)
|
||||
|
||||
# Vérifier si le jeu a au moins une région incluse
|
||||
has_included_region = False
|
||||
@@ -244,7 +263,7 @@ class GameFilters:
|
||||
continue
|
||||
|
||||
# Filtrer les non-release
|
||||
if self.hide_non_release and self.is_non_release_game(game_name):
|
||||
if self.hide_non_release and self.get_cached_non_release(game):
|
||||
continue
|
||||
|
||||
filtered_games.append(game)
|
||||
@@ -255,13 +274,12 @@ class GameFilters:
|
||||
|
||||
return filtered_games
|
||||
|
||||
def _apply_one_rom_per_game(self, games: List[Tuple]) -> List[Tuple]:
|
||||
def _apply_one_rom_per_game(self, games: List[Game]) -> List[Game]:
|
||||
"""Garde seulement une ROM par jeu selon la priorité de région"""
|
||||
games_by_base = {}
|
||||
|
||||
for game in games:
|
||||
game_name = game[0]
|
||||
base_name = self.get_base_game_name(game_name)
|
||||
base_name = self.get_cached_base_name(game)
|
||||
|
||||
if base_name not in games_by_base:
|
||||
games_by_base[base_name] = []
|
||||
@@ -276,7 +294,7 @@ class GameFilters:
|
||||
else:
|
||||
# Trier par priorité de région
|
||||
sorted_games = sorted(game_list,
|
||||
key=lambda g: self.get_region_priority(g[0]))
|
||||
key=self.get_region_priority)
|
||||
result.append(sorted_games[0])
|
||||
|
||||
return result
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
import config
|
||||
from datetime import datetime
|
||||
|
||||
@@ -119,18 +120,39 @@ def clear_history():
|
||||
try:
|
||||
# Charger l'historique actuel
|
||||
current_history = load_history()
|
||||
|
||||
# Conserver uniquement les entrées avec statut actif (téléchargement, extraction ou conversion en cours)
|
||||
# Supporter les deux variantes de statut (anglais et français)
|
||||
|
||||
active_statuses = {"Downloading", "Téléchargement", "downloading", "Extracting", "Converting", "Queued"}
|
||||
preserved_entries = [
|
||||
entry for entry in current_history
|
||||
if entry.get("status") in active_statuses
|
||||
]
|
||||
|
||||
# Sauvegarder l'historique filtré
|
||||
with open(history_path, "w", encoding='utf-8') as f:
|
||||
json.dump(preserved_entries, f, indent=2, ensure_ascii=False)
|
||||
|
||||
active_task_ids = set(getattr(config, 'download_tasks', {}).keys())
|
||||
active_progress_urls = set(getattr(config, 'download_progress', {}).keys())
|
||||
queued_urls = {
|
||||
item.get("url") for item in getattr(config, 'download_queue', [])
|
||||
if isinstance(item, dict) and item.get("url")
|
||||
}
|
||||
queued_task_ids = {
|
||||
item.get("task_id") for item in getattr(config, 'download_queue', [])
|
||||
if isinstance(item, dict) and item.get("task_id")
|
||||
}
|
||||
|
||||
def is_truly_active(entry):
|
||||
if not isinstance(entry, dict):
|
||||
return False
|
||||
|
||||
status = entry.get("status")
|
||||
if status not in active_statuses:
|
||||
return False
|
||||
|
||||
task_id = entry.get("task_id")
|
||||
url = entry.get("url")
|
||||
|
||||
if status == "Queued":
|
||||
return task_id in queued_task_ids or url in queued_urls
|
||||
|
||||
return task_id in active_task_ids or url in active_progress_urls
|
||||
|
||||
preserved_entries = [entry for entry in current_history if is_truly_active(entry)]
|
||||
|
||||
save_history(preserved_entries)
|
||||
|
||||
removed_count = len(current_history) - len(preserved_entries)
|
||||
logger.info(f"Historique vidé : {history_path} ({removed_count} entrées supprimées, {len(preserved_entries)} conservées)")
|
||||
@@ -140,6 +162,118 @@ def clear_history():
|
||||
|
||||
# ==================== GESTION DES JEUX TÉLÉCHARGÉS ====================
|
||||
|
||||
IGNORED_ROM_SCAN_EXTENSIONS = {
|
||||
'.bak', '.bmp', '.db', '.gif', '.ini', '.jpeg', '.jpg', '.json', '.log', '.mp4',
|
||||
'.nfo', '.pdf', '.png', '.srm', '.sav', '.state', '.svg', '.txt', '.webp', '.xml'
|
||||
}
|
||||
|
||||
|
||||
def normalize_downloaded_game_name(game_name):
|
||||
"""Normalise un nom de jeu pour les comparaisons en ignorant extension et tags."""
|
||||
if not isinstance(game_name, str):
|
||||
return ""
|
||||
|
||||
normalized = os.path.basename(game_name.strip())
|
||||
if not normalized:
|
||||
return ""
|
||||
|
||||
normalized = os.path.splitext(normalized)[0]
|
||||
normalized = re.sub(r'\s*[\[(][^\])]*[\])]', '', normalized)
|
||||
normalized = re.sub(r'\s+', ' ', normalized)
|
||||
return normalized.strip().lower()
|
||||
|
||||
|
||||
def _normalize_downloaded_games_dict(downloaded):
|
||||
"""Normalise la structure de downloaded_games.json en restant rétrocompatible."""
|
||||
normalized_downloaded = {}
|
||||
|
||||
if not isinstance(downloaded, dict):
|
||||
return normalized_downloaded
|
||||
|
||||
for platform_name, games in downloaded.items():
|
||||
if not isinstance(platform_name, str):
|
||||
continue
|
||||
if not isinstance(games, dict):
|
||||
continue
|
||||
|
||||
normalized_games = {}
|
||||
for game_name, metadata in games.items():
|
||||
normalized_name = normalize_downloaded_game_name(game_name)
|
||||
if not normalized_name:
|
||||
continue
|
||||
normalized_games[normalized_name] = metadata if isinstance(metadata, dict) else {}
|
||||
|
||||
if normalized_games:
|
||||
normalized_downloaded[platform_name] = normalized_games
|
||||
|
||||
return normalized_downloaded
|
||||
|
||||
|
||||
def _count_downloaded_games(downloaded_games_dict):
|
||||
return sum(len(games) for games in downloaded_games_dict.values() if isinstance(games, dict))
|
||||
|
||||
|
||||
def scan_roms_for_downloaded_games():
|
||||
"""Scanne les dossiers ROMs et ajoute les jeux trouvés à downloaded_games.json."""
|
||||
from utils import load_games
|
||||
|
||||
downloaded = _normalize_downloaded_games_dict(getattr(config, 'downloaded_games', {}))
|
||||
platform_dicts = list(getattr(config, 'platform_dicts', []) or [])
|
||||
|
||||
if not platform_dicts:
|
||||
return 0, 0
|
||||
|
||||
scanned_platforms = 0
|
||||
added_games = 0
|
||||
|
||||
for platform_entry in platform_dicts:
|
||||
if not isinstance(platform_entry, dict):
|
||||
continue
|
||||
|
||||
platform_name = (platform_entry.get('platform_name') or '').strip()
|
||||
folder_name = (platform_entry.get('folder') or '').strip()
|
||||
if not platform_name or not folder_name:
|
||||
continue
|
||||
|
||||
roms_path = os.path.join(config.ROMS_FOLDER, folder_name)
|
||||
if not os.path.isdir(roms_path):
|
||||
continue
|
||||
|
||||
available_games = load_games(platform_name)
|
||||
available_names = {
|
||||
normalize_downloaded_game_name(game.name)
|
||||
for game in available_games
|
||||
if normalize_downloaded_game_name(game.name)
|
||||
}
|
||||
if not available_names:
|
||||
continue
|
||||
|
||||
platform_games = downloaded.setdefault(platform_name, {})
|
||||
scanned_platforms += 1
|
||||
|
||||
for root, _, filenames in os.walk(roms_path):
|
||||
for filename in filenames:
|
||||
file_ext = os.path.splitext(filename)[1].lower()
|
||||
if file_ext in IGNORED_ROM_SCAN_EXTENSIONS:
|
||||
continue
|
||||
|
||||
normalized_name = normalize_downloaded_game_name(filename)
|
||||
if not normalized_name or normalized_name not in available_names:
|
||||
continue
|
||||
|
||||
if normalized_name not in platform_games:
|
||||
platform_games[normalized_name] = {}
|
||||
added_games += 1
|
||||
|
||||
config.downloaded_games = downloaded
|
||||
save_downloaded_games(downloaded)
|
||||
logger.info(
|
||||
"Scan ROMs terminé : %s jeux ajoutés sur %s plateformes",
|
||||
added_games,
|
||||
scanned_platforms,
|
||||
)
|
||||
return added_games, scanned_platforms
|
||||
|
||||
def load_downloaded_games():
|
||||
"""Charge la liste des jeux déjà téléchargés depuis downloaded_games.json."""
|
||||
downloaded_path = getattr(config, 'DOWNLOADED_GAMES_PATH')
|
||||
@@ -162,9 +296,10 @@ def load_downloaded_games():
|
||||
if not isinstance(downloaded, dict):
|
||||
logger.warning(f"Format downloaded_games.json invalide (pas un dict)")
|
||||
return {}
|
||||
|
||||
logger.debug(f"Jeux téléchargés chargés : {sum(len(v) for v in downloaded.values())} jeux")
|
||||
return downloaded
|
||||
|
||||
normalized_downloaded = _normalize_downloaded_games_dict(downloaded)
|
||||
logger.debug(f"Jeux téléchargés chargés : {_count_downloaded_games(normalized_downloaded)} jeux")
|
||||
return normalized_downloaded
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
logger.error(f"Erreur lors de la lecture de {downloaded_path} : {e}")
|
||||
return {}
|
||||
@@ -177,17 +312,18 @@ def save_downloaded_games(downloaded_games_dict):
|
||||
"""Sauvegarde la liste des jeux téléchargés dans downloaded_games.json."""
|
||||
downloaded_path = getattr(config, 'DOWNLOADED_GAMES_PATH')
|
||||
try:
|
||||
normalized_downloaded = _normalize_downloaded_games_dict(downloaded_games_dict)
|
||||
os.makedirs(os.path.dirname(downloaded_path), exist_ok=True)
|
||||
|
||||
# Écriture atomique
|
||||
temp_path = downloaded_path + '.tmp'
|
||||
with open(temp_path, "w", encoding='utf-8') as f:
|
||||
json.dump(downloaded_games_dict, f, indent=2, ensure_ascii=False)
|
||||
json.dump(normalized_downloaded, f, indent=2, ensure_ascii=False)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
|
||||
os.replace(temp_path, downloaded_path)
|
||||
logger.debug(f"Jeux téléchargés sauvegardés : {sum(len(v) for v in downloaded_games_dict.values())} jeux")
|
||||
logger.debug(f"Jeux téléchargés sauvegardés : {_count_downloaded_games(normalized_downloaded)} jeux")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'écriture de {downloaded_path} : {e}")
|
||||
try:
|
||||
@@ -200,21 +336,22 @@ def save_downloaded_games(downloaded_games_dict):
|
||||
def mark_game_as_downloaded(platform_name, game_name, file_size=None):
|
||||
"""Marque un jeu comme téléchargé."""
|
||||
downloaded = config.downloaded_games
|
||||
normalized_name = normalize_downloaded_game_name(game_name)
|
||||
if not normalized_name:
|
||||
return
|
||||
|
||||
if platform_name not in downloaded:
|
||||
downloaded[platform_name] = {}
|
||||
|
||||
downloaded[platform_name][game_name] = {
|
||||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"size": file_size or "N/A"
|
||||
}
|
||||
downloaded[platform_name][normalized_name] = {}
|
||||
|
||||
# Sauvegarder immédiatement
|
||||
save_downloaded_games(downloaded)
|
||||
logger.info(f"Jeu marqué comme téléchargé : {platform_name} / {game_name}")
|
||||
logger.info(f"Jeu marqué comme téléchargé : {platform_name} / {normalized_name}")
|
||||
|
||||
|
||||
def is_game_downloaded(platform_name, game_name):
|
||||
"""Vérifie si un jeu a déjà été téléchargé."""
|
||||
downloaded = config.downloaded_games
|
||||
return platform_name in downloaded and game_name in downloaded.get(platform_name, {})
|
||||
normalized_name = normalize_downloaded_game_name(game_name)
|
||||
return bool(normalized_name) and platform_name in downloaded and normalized_name in downloaded.get(platform_name, {})
|
||||
|
||||
@@ -114,14 +114,14 @@ def get_text(key, default=None):
|
||||
pass
|
||||
return str(default) if default is not None else str(key)
|
||||
|
||||
def get_available_languages():
|
||||
def get_available_languages() -> list[str]:
|
||||
"""Récupère la liste des langues disponibles."""
|
||||
|
||||
if not os.path.exists(config.LANGUAGES_FOLDER):
|
||||
logger.warning(f"Dossier des langues {config.LANGUAGES_FOLDER} non trouvé")
|
||||
return []
|
||||
|
||||
languages = []
|
||||
languages: list[str] = []
|
||||
for file in os.listdir(config.LANGUAGES_FOLDER):
|
||||
if file.endswith(".json"):
|
||||
lang_code = os.path.splitext(file)[0]
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"error_no_internet": "Keine Internetverbindung. Überprüfe dein Netzwerk.",
|
||||
"error_api_key": "Achtung, du musst deinen API-Schlüssel (nur Premium) in der Datei {0} eingeben",
|
||||
"error_invalid_download_data": "Ungültige Downloaddaten",
|
||||
"popup_torrent_in_maintenance": "Torrent in Wartung, bitte warten",
|
||||
"error_delete_sources": "Fehler beim Löschen der Datei systems_list.json oder Ordner",
|
||||
"platform_no_platform": "Keine Plattform",
|
||||
"platform_page": "Seite {0}/{1}",
|
||||
@@ -24,7 +25,11 @@
|
||||
"game_count": "{0} ({1} Spiele)",
|
||||
"game_filter": "Aktiver Filter: {0}",
|
||||
"game_search": "Filtern: {0}",
|
||||
"global_search_title": "Globale Suche: {0}",
|
||||
"global_search_empty_query": "Geben Sie einen Namen ein, um alle Systeme zu durchsuchen",
|
||||
"global_search_no_results": "Keine Ergebnisse fur: {0}",
|
||||
"game_header_name": "Name",
|
||||
"game_header_ext": "Ext",
|
||||
"game_header_size": "Größe",
|
||||
"history_title": "Downloads ({0})",
|
||||
"history_empty": "Keine Downloads im Verlauf",
|
||||
@@ -42,6 +47,9 @@
|
||||
"free_mode_submitting": "[Kostenloser Modus] Formular wird gesendet...",
|
||||
"free_mode_link_found": "[Kostenloser Modus] Link gefunden: {0}...",
|
||||
"free_mode_completed": "[Kostenloser Modus] Abgeschlossen: {0}",
|
||||
"free_mode_guest_slots_unavailable": "1fichier: Der kostenlose Gast-Download ist vorübergehend nicht verfügbar (alle Slots sind belegt). Bitte versuchen Sie es später erneut.",
|
||||
"free_mode_unavailable_in_app": "1fichier: Dieser Download ist derzeit in der Anwendung nicht verfügbar. Bitte versuchen Sie es später erneut.",
|
||||
"free_mode_premium_advice": "Für unbegrenzte Downloads jederzeit und mit voller Geschwindigkeit benötigen Sie ein Premium-Konto oder einen Debrid-Dienst und müssen dessen API-Schlüssel in RGSX eintragen.",
|
||||
"download_status": "{0}: {1}",
|
||||
"download_canceled": "Download vom Benutzer abgebrochen.",
|
||||
"download_removed_from_queue": "Aus der Download-Warteschlange entfernt",
|
||||
@@ -52,6 +60,8 @@
|
||||
"confirm_exit_with_downloads": "Achtung: {0} Download(s) laufen. Trotzdem beenden?",
|
||||
"confirm_clear_history": "Verlauf löschen?",
|
||||
"confirm_redownload_cache": "Spieleliste aktualisieren?",
|
||||
"gamelist_update_prompt_with_date": "Die Spieleliste wurde seit mehr als {0} Tagen nicht aktualisiert (letzte Aktualisierung: {1}). Die neueste Version herunterladen?",
|
||||
"gamelist_update_prompt_first_time": "Möchten Sie die neueste Spieleliste herunterladen?",
|
||||
"popup_redownload_success": "Cache gelöscht, bitte die Anwendung neu starten",
|
||||
"popup_no_cache": "Kein Cache gefunden.\nBitte starte die Anwendung neu, um die Spiele zu laden.",
|
||||
"popup_countdown": "Diese Nachricht schließt in {0} Sekunde{1}",
|
||||
@@ -60,6 +70,12 @@
|
||||
"language_changed": "Sprache geändert zu {0}",
|
||||
"menu_controls": "Steuerung",
|
||||
"menu_remap_controls": "Steuerung neu zuordnen",
|
||||
"menu_nintendo_layout_on": "Nintendo-Controller-Layout",
|
||||
"menu_nintendo_layout_off": "Xbox-Controller-Layout",
|
||||
"instruction_nintendo_layout": "Invertiert die angezeigten Steuerungen, um das Layout anzupassen",
|
||||
"controller_style_label": "Controller-Stil :",
|
||||
"controller_style_nintendo": "Nintendo",
|
||||
"controller_style_xbox": "Xbox",
|
||||
"menu_history": "Verlauf",
|
||||
"menu_language": "Sprache",
|
||||
"menu_accessibility": "Barrierefreiheit",
|
||||
@@ -103,6 +119,7 @@
|
||||
"controls_action_clear_history": "Verlauf leeren",
|
||||
"controls_action_history": "Verlauf / Downloads",
|
||||
"controls_action_close_history": "Verlauf schließen",
|
||||
"history_column_folder": "Ordner",
|
||||
"controls_action_queue": "Warteschlange",
|
||||
"controls_action_delete": "Löschen",
|
||||
"controls_action_space": "Leerzeichen",
|
||||
@@ -136,6 +153,8 @@
|
||||
"controls_confirm_select": "Bestätigen/Auswählen",
|
||||
"controls_cancel_back": "Abbrechen/Zurück",
|
||||
"controls_filter_search": "Filtern/Suchen",
|
||||
"controls_action_edit_search": "Suche bearbeiten",
|
||||
"controls_action_show_results": "Ergebnisse zeigen",
|
||||
"network_download_failed": "Download nach {0} Versuchen fehlgeschlagen",
|
||||
"network_api_error": "Fehler bei der API-Anfrage, der Schlüssel könnte falsch sein: {0}",
|
||||
"network_download_error": "Downloadfehler {0}: {1}",
|
||||
@@ -181,7 +200,14 @@
|
||||
"status_present": "Vorhanden",
|
||||
"status_missing": "Fehlt",
|
||||
"menu_api_keys_status": "API-Schlüssel",
|
||||
"menu_connection_status": "Verbindungsstatus",
|
||||
"api_keys_status_title": "Status der API-Schlüssel",
|
||||
"connection_status_title": "Verbindungsstatus",
|
||||
"connection_status_category_updates": "App-/Gamelist-Update",
|
||||
"connection_status_category_sources": "Spielquellen",
|
||||
"connection_status_checking": "Prüfe...",
|
||||
"connection_status_progress": "Prüfe... {done}/{total}",
|
||||
"connection_status_last_check": "Letzte Prüfung: {time}",
|
||||
"menu_games": "Spiele",
|
||||
"api_keys_hint_manage": "Legen Sie Ihre Schlüssel in {path}",
|
||||
"api_key_empty_suffix": "leer",
|
||||
@@ -217,11 +243,24 @@
|
||||
"instruction_games_history": "Vergangene Downloads und Status anzeigen",
|
||||
"instruction_games_source_mode": "Zwischen RGSX oder eigener Quellliste wechseln",
|
||||
"instruction_games_update_cache": "Aktuelle Spieleliste erneut herunterladen & aktualisieren",
|
||||
"instruction_games_scan_owned": "ROM-Ordner scannen und bereits vorhandene Spiele markieren",
|
||||
"instruction_settings_music": "Hintergrundmusik aktivieren oder deaktivieren",
|
||||
"instruction_settings_symlink": "Verwendung von Symlinks für Installationen umschalten",
|
||||
"instruction_settings_auto_extract": "Automatische Archivextraktion nach Download aktivieren/deaktivieren",
|
||||
"instruction_settings_roms_folder": "Standard-Download-Verzeichnis für ROMs ändern",
|
||||
"instruction_settings_api_keys": "Gefundene Premium-API-Schlüssel ansehen",
|
||||
"instruction_settings_connection_status": "Zugriff auf Update- und Quellen-Seiten prüfen",
|
||||
"instruction_settings_web_service": "Web-Dienst Autostart beim Booten aktivieren/deaktivieren",
|
||||
"instruction_settings_custom_dns": "Custom DNS (Cloudflare 1.1.1.1) beim Booten aktivieren/deaktivieren",
|
||||
"settings_auto_extract": "Auto-Extraktion Archive",
|
||||
"settings_auto_extract_enabled": "Aktiviert",
|
||||
"settings_auto_extract_disabled": "Deaktiviert",
|
||||
"settings_roms_folder": "ROMs-Ordner",
|
||||
"settings_roms_folder_default": "Standard",
|
||||
"roms_folder_set": "ROMs-Ordner festgelegt: {0}",
|
||||
"roms_folder_set_restart": "ROMs-Ordner festgelegt: {0}\nNeustart erforderlich!",
|
||||
"roms_folder_reset": "ROMs-Ordner auf Standard zurückgesetzt\nNeustart erforderlich!",
|
||||
"folder_browser_title_roms_root": "Standard-ROMs-Ordner auswählen",
|
||||
"settings_web_service": "Web-Dienst beim Booten",
|
||||
"settings_web_service_enabled": "Aktiviert",
|
||||
"settings_web_service_disabled": "Deaktiviert",
|
||||
@@ -231,6 +270,9 @@
|
||||
"settings_web_service_success_disabled": "Web-Dienst beim Booten deaktiviert",
|
||||
"settings_web_service_error": "Fehler: {0}",
|
||||
"settings_custom_dns": "Custom DNS beim Booten",
|
||||
"menu_scan_owned_roms": "Vorhandene ROMs scannen",
|
||||
"popup_scan_owned_roms_done": "ROM-Scan abgeschlossen: {0} Spiele auf {1} Plattformen hinzugefügt",
|
||||
"popup_scan_owned_roms_error": "ROM-Scan-Fehler: {0}",
|
||||
"settings_custom_dns_enabled": "Aktiviert",
|
||||
"settings_custom_dns_disabled": "Deaktiviert",
|
||||
"settings_custom_dns_enabling": "Custom DNS wird aktiviert...",
|
||||
@@ -264,9 +306,12 @@
|
||||
"history_option_scraper": "Metadaten abrufen",
|
||||
"history_option_remove_from_queue": "Aus Warteschlange entfernen",
|
||||
"history_option_cancel_download": "Download abbrechen",
|
||||
"history_option_pause_download": "Download pausieren",
|
||||
"history_option_resume_download": "Download fortsetzen",
|
||||
"history_option_delete_game": "Spiel löschen",
|
||||
"history_option_error_info": "Fehlerdetails",
|
||||
"history_option_retry": "Download wiederholen",
|
||||
"history_move_action": "Verschieben",
|
||||
"history_option_back": "Zurück",
|
||||
"history_folder_path_label": "Zielpfad:",
|
||||
"history_scraper_not_implemented": "Scraper noch nicht implementiert",
|
||||
@@ -276,6 +321,8 @@
|
||||
"history_extracted": "Extrahiert",
|
||||
"history_delete_success": "Spiel erfolgreich gelöscht",
|
||||
"history_delete_error": "Fehler beim Löschen des Spiels: {0}",
|
||||
"history_move_success": "{0} Datei(en) verschoben nach: {1}",
|
||||
"history_move_error": "Fehler beim Verschieben: {0}",
|
||||
"history_error_details_title": "Fehlerdetails",
|
||||
"history_no_error_message": "Keine Fehlermeldung verfügbar",
|
||||
"web_title": "RGSX Web-Oberfläche",
|
||||
@@ -326,6 +373,9 @@
|
||||
"web_settings_source_mode": "Spielequelle",
|
||||
"web_settings_custom_url": "Benutzerdefinierte URL",
|
||||
"web_settings_custom_url_placeholder": "https://beispiel.com/spiele.zip",
|
||||
"web_settings_auto_extract": "Archive nach dem Download automatisch entpacken",
|
||||
"web_settings_web_service": "Webdienst beim Booten starten",
|
||||
"web_settings_custom_dns": "Benutzerdefinierten DNS beim Booten aktivieren",
|
||||
"web_settings_save": "Einstellungen speichern",
|
||||
"web_settings_saved": "Einstellungen erfolgreich gespeichert!",
|
||||
"web_settings_saved_restart": "Einstellungen erfolgreich gespeichert!\\n\\n⚠️ Einige Einstellungen erfordern einen Serverneustart:\\n- Benutzerdefinierter ROMs-Ordner\\n- Sprache\\n\\nBitte starten Sie den Webserver neu, um diese Änderungen anzuwenden.",
|
||||
@@ -349,6 +399,7 @@
|
||||
"web_history_status_completed": "Abgeschlossen",
|
||||
"web_history_status_error": "Fehler",
|
||||
"web_settings_os": "Betriebssystem",
|
||||
"web_system_info_title": "Systeminformationen",
|
||||
"web_settings_platforms_count": "Anzahl der Plattformen",
|
||||
"web_settings_show_unsupported": "Nicht unterstützte Plattformen anzeigen (System fehlt in es_systems.cfg)",
|
||||
"web_settings_allow_unknown": "Unbekannte Erweiterungen erlauben (keine Warnungen anzeigen)",
|
||||
@@ -427,5 +478,24 @@
|
||||
"filter_reset_filters": "Zurücksetzen",
|
||||
"filter_back": "Zurück",
|
||||
"filter_active": "Filter aktiv",
|
||||
"filter_games_shown": "{0} Spiel(e) angezeigt"
|
||||
"filter_games_shown": "{0} Spiel(e) angezeigt",
|
||||
"platform_folder_config_current": "Download-Ordner für {0} konfigurieren\nAktuell: {1}",
|
||||
"platform_folder_config_default": "Download-Ordner für {0} konfigurieren\nStandardordner wird verwendet",
|
||||
"platform_folder_show_current": "Aktuellen Pfad anzeigen",
|
||||
"platform_folder_browse": "Durchsuchen",
|
||||
"platform_folder_reset": "Auf Standard zurücksetzen",
|
||||
"platform_folder_set": "Ordner für {0} festgelegt: {1}",
|
||||
"platform_folder_default_path": "Standard: {0}",
|
||||
"folder_browser_title": "Ordner für {0} auswählen",
|
||||
"folder_browser_title_history_move": "Zielordner auswählen",
|
||||
"folder_browser_parent": "Übergeordneter Ordner",
|
||||
"folder_browser_enter": "Öffnen",
|
||||
"folder_browser_select": "Auswählen",
|
||||
"folder_new_folder": "Neuer Ordner",
|
||||
"folder_new_title": "Neuen Ordner erstellen",
|
||||
"folder_new_confirm": "Erstellen",
|
||||
"folder_created": "Ordner erstellt: {0}",
|
||||
"folder_create_error": "Fehler beim Erstellen: {0}",
|
||||
"controls_action_select_char": "Zeichen",
|
||||
"folder_browser_browse": "Durchsuchen"
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
"error_no_internet": "No Internet connection. Check your network.",
|
||||
"error_api_key": "Please enter your API key (premium only) in the file {0}",
|
||||
"error_invalid_download_data": "Invalid download data",
|
||||
"popup_torrent_in_maintenance": "Torrent under maintenance, please wait",
|
||||
"error_delete_sources": "Error deleting systems_list.json file or folders",
|
||||
"platform_no_platform": "No platform",
|
||||
"platform_page": "Page {0}/{1}",
|
||||
@@ -24,7 +25,11 @@
|
||||
"game_count": "{0} ({1} games)",
|
||||
"game_filter": "Active filter: {0}",
|
||||
"game_search": "Filter: {0}",
|
||||
"global_search_title": "Global search: {0}",
|
||||
"global_search_empty_query": "Type a game name to search across all systems",
|
||||
"global_search_no_results": "No results for: {0}",
|
||||
"game_header_name": "Name",
|
||||
"game_header_ext": "Ext",
|
||||
"game_header_size": "Size",
|
||||
"history_title": "Downloads ({0})",
|
||||
"history_empty": "No downloads in history",
|
||||
@@ -42,6 +47,9 @@
|
||||
"free_mode_submitting": "[Free mode] Submitting form...",
|
||||
"free_mode_link_found": "[Free mode] Link found: {0}...",
|
||||
"free_mode_completed": "[Free mode] Completed: {0}",
|
||||
"free_mode_guest_slots_unavailable": "1fichier: free guest download is temporarily unavailable (all slots are currently in use). Please try again later.",
|
||||
"free_mode_unavailable_in_app": "1fichier: this download is not available in the application right now. Please try again later.",
|
||||
"free_mode_premium_advice": "For unlimited, on-demand, full-speed downloads, you need a premium account or debrid service and must enter its API key in RGSX.",
|
||||
"download_status": "{0}: {1}",
|
||||
"download_canceled": "Download canceled by user.",
|
||||
"download_removed_from_queue": "Removed from download queue",
|
||||
@@ -52,6 +60,8 @@
|
||||
"confirm_exit_with_downloads": "Attention: {0} download(s) in progress. Quit anyway?",
|
||||
"confirm_clear_history": "Clear history?",
|
||||
"confirm_redownload_cache": "Update games list?",
|
||||
"gamelist_update_prompt_with_date": "Game list hasn't been updated for more than {0} days (last update: {1}). Download the latest version?",
|
||||
"gamelist_update_prompt_first_time": "Would you like to download the latest game list?",
|
||||
"popup_redownload_success": "Cache cleared, please restart the application",
|
||||
"popup_no_cache": "No cache found.\nPlease restart the application to load games.",
|
||||
"popup_countdown": "This message will close in {0} second{1}",
|
||||
@@ -60,6 +70,9 @@
|
||||
"language_changed": "Language changed to {0}",
|
||||
"menu_controls": "Controls",
|
||||
"menu_remap_controls": "Remap controls",
|
||||
"menu_nintendo_layout_on": "Nintendo Controller layout",
|
||||
"menu_nintendo_layout_off": "Xbox Controller layout",
|
||||
"instruction_nintendo_layout": "Inverts the displayed controls to match layout",
|
||||
"menu_history": "History",
|
||||
"menu_language": "Language",
|
||||
"menu_accessibility": "Accessibility",
|
||||
@@ -107,6 +120,7 @@
|
||||
"support_dialog_error": "Error generating support file:\n{0}\n\nPress {1} to return to the menu.",
|
||||
"controls_action_history": "History / Downloads",
|
||||
"controls_action_close_history": "Close History",
|
||||
"history_column_folder": "Folder",
|
||||
"network_checking_updates": "Update in progress please wait...",
|
||||
"network_update_available": "Update available: {0}",
|
||||
"network_extracting_update": "Extracting update...",
|
||||
@@ -160,6 +174,8 @@
|
||||
"controls_confirm_select": "Confirm/Select",
|
||||
"controls_cancel_back": "Cancel/Back",
|
||||
"controls_filter_search": "Filter/Search",
|
||||
"controls_action_edit_search": "Edit search",
|
||||
"controls_action_show_results": "Show results",
|
||||
"symlink_option_enabled": "Symlink option enabled",
|
||||
"symlink_option_disabled": "Symlink option disabled",
|
||||
"menu_games_source_prefix": "Game source",
|
||||
@@ -183,7 +199,14 @@
|
||||
"status_present": "Present",
|
||||
"status_missing": "Missing",
|
||||
"menu_api_keys_status": "API Keys",
|
||||
"menu_connection_status": "Connection status",
|
||||
"api_keys_status_title": "API Keys Status",
|
||||
"connection_status_title": "Connection status",
|
||||
"connection_status_category_updates": "App/Gamelist update",
|
||||
"connection_status_category_sources": "Game sources",
|
||||
"connection_status_checking": "Checking...",
|
||||
"connection_status_progress": "Checking... {done}/{total}",
|
||||
"connection_status_last_check": "Last check: {time}",
|
||||
"menu_games": "Games",
|
||||
"api_keys_hint_manage": "Put your keys in {path}",
|
||||
"api_key_empty_suffix": "empty",
|
||||
@@ -203,6 +226,9 @@
|
||||
"instruction_quit_app": "Exit the RGSX application",
|
||||
"instruction_quit_restart": "Restart the RGSX application",
|
||||
"instruction_controls_help": "Show full controller & keyboard reference",
|
||||
"controller_style_label": "Controller Style :",
|
||||
"controller_style_nintendo": "Nintendo",
|
||||
"controller_style_xbox": "Xbox",
|
||||
"instruction_controls_remap": "Change button / key bindings",
|
||||
"instruction_generic_back": "Return to the previous menu",
|
||||
"instruction_display_layout": "Cycle grid dimensions (columns × rows)",
|
||||
@@ -219,11 +245,24 @@
|
||||
"instruction_games_history": "List past downloads and statuses",
|
||||
"instruction_games_source_mode": "Switch between RGSX or your own custom list source",
|
||||
"instruction_games_update_cache": "Redownload & refresh current games list",
|
||||
"instruction_games_scan_owned": "Scan your ROM folders and mark matching games as already owned",
|
||||
"instruction_settings_music": "Enable or disable background music playback",
|
||||
"instruction_settings_symlink": "Toggle using filesystem symlinks for installs",
|
||||
"instruction_settings_auto_extract": "Toggle automatic archive extraction after download",
|
||||
"instruction_settings_roms_folder": "Change the default ROMs download directory",
|
||||
"instruction_settings_api_keys": "See detected premium provider API keys",
|
||||
"instruction_settings_connection_status": "Check access to update and source sites",
|
||||
"instruction_settings_web_service": "Enable/disable web service auto-start at boot",
|
||||
"instruction_settings_custom_dns": "Enable/disable custom DNS (Cloudflare 1.1.1.1) at boot",
|
||||
"settings_auto_extract": "Auto Extract Archives",
|
||||
"settings_auto_extract_enabled": "Enabled",
|
||||
"settings_auto_extract_disabled": "Disabled",
|
||||
"settings_roms_folder": "ROMs Folder",
|
||||
"settings_roms_folder_default": "Default",
|
||||
"roms_folder_set": "ROMs folder set: {0}",
|
||||
"roms_folder_set_restart": "ROMs folder set: {0}\nRestart required to apply!",
|
||||
"roms_folder_reset": "ROMs folder reset to default\nRestart required to apply!",
|
||||
"folder_browser_title_roms_root": "Select default ROMs folder",
|
||||
"settings_web_service": "Web Service at Boot",
|
||||
"settings_web_service_enabled": "Enabled",
|
||||
"settings_web_service_disabled": "Disabled",
|
||||
@@ -266,9 +305,15 @@
|
||||
"history_option_scraper": "Scrape metadata",
|
||||
"history_option_remove_from_queue": "Remove from queue",
|
||||
"history_option_cancel_download": "Cancel download",
|
||||
"history_option_pause_download": "Pause download",
|
||||
"history_option_resume_download": "Resume download",
|
||||
"history_option_delete_game": "Delete game",
|
||||
"history_option_error_info": "Error details",
|
||||
"history_option_retry": "Retry download",
|
||||
"history_move_action": "Move",
|
||||
"menu_scan_owned_roms": "Scan owned ROMs",
|
||||
"popup_scan_owned_roms_done": "ROM scan complete: {0} games added across {1} platforms",
|
||||
"popup_scan_owned_roms_error": "ROM scan error: {0}",
|
||||
"history_option_back": "Back",
|
||||
"history_folder_path_label": "Destination path:",
|
||||
"history_scraper_not_implemented": "Scraper not yet implemented",
|
||||
@@ -278,6 +323,8 @@
|
||||
"history_extracted": "Extracted",
|
||||
"history_delete_success": "Game deleted successfully",
|
||||
"history_delete_error": "Error deleting game: {0}",
|
||||
"history_move_success": "Moved {0} file(s) to: {1}",
|
||||
"history_move_error": "Error while moving files: {0}",
|
||||
"history_error_details_title": "Error Details",
|
||||
"history_no_error_message": "No error message available",
|
||||
"web_title": "RGSX Web Interface",
|
||||
@@ -328,6 +375,9 @@
|
||||
"web_settings_source_mode": "Games source",
|
||||
"web_settings_custom_url": "Custom URL",
|
||||
"web_settings_custom_url_placeholder": "Let empty for local /saves/ports/rgsx/games.zip or use a direct URL like https://example.com/games.zip",
|
||||
"web_settings_auto_extract": "Auto-extract archives after download",
|
||||
"web_settings_web_service": "Start web service at boot",
|
||||
"web_settings_custom_dns": "Enable custom DNS at boot",
|
||||
"web_settings_save": "Save Settings",
|
||||
"web_settings_saved": "Settings saved successfully!",
|
||||
"web_settings_saved_restart": "Settings saved successfully!\\n\\n⚠️ Some settings require a server restart:\\n- Custom ROMs folder\\n- Language\\n\\nPlease restart the web server to apply these changes.",
|
||||
@@ -351,6 +401,7 @@
|
||||
"web_history_status_completed": "Completed",
|
||||
"web_history_status_error": "Error",
|
||||
"web_settings_os": "Operating System",
|
||||
"web_system_info_title": "System Information",
|
||||
"web_settings_platforms_count": "Number of platforms",
|
||||
"web_settings_show_unsupported": "Show unsupported platforms (system not found in es_systems.cfg)",
|
||||
"web_settings_allow_unknown": "Allow unknown extensions (don't show warnings)",
|
||||
@@ -427,5 +478,24 @@
|
||||
"filter_reset_filters": "Reset",
|
||||
"filter_back": "Back",
|
||||
"filter_active": "Filter active",
|
||||
"filter_games_shown": "{0} game(s) shown"
|
||||
"filter_games_shown": "{0} game(s) shown",
|
||||
"platform_folder_config_current": "Configure download folder for {0}\nCurrent: {1}",
|
||||
"platform_folder_config_default": "Configure download folder for {0}\nUsing default location",
|
||||
"platform_folder_show_current": "Show current path",
|
||||
"platform_folder_browse": "Browse",
|
||||
"platform_folder_reset": "Reset to default",
|
||||
"platform_folder_set": "Folder set for {0}: {1}",
|
||||
"platform_folder_default_path": "Default: {0}",
|
||||
"folder_browser_title": "Select folder for {0}",
|
||||
"folder_browser_title_history_move": "Select destination folder",
|
||||
"folder_browser_parent": "Parent folder",
|
||||
"folder_browser_enter": "Enter",
|
||||
"folder_browser_select": "Select",
|
||||
"folder_new_folder": "New folder",
|
||||
"folder_new_title": "Create New Folder",
|
||||
"folder_new_confirm": "Create",
|
||||
"folder_created": "Folder created: {0}",
|
||||
"folder_create_error": "Error creating folder: {0}",
|
||||
"controls_action_select_char": "Add char",
|
||||
"folder_browser_browse": "Browse"
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
"error_no_internet": "Sin conexión a Internet. Verifica tu red.",
|
||||
"error_api_key": "Atención, debes ingresar tu clave API (solo premium) en el archivo {0}",
|
||||
"error_invalid_download_data": "Datos de descarga no válidos",
|
||||
"popup_torrent_in_maintenance": "Torrent en mantenimiento, por favor espere",
|
||||
"error_delete_sources": "Error al eliminar el archivo systems_list.json o carpetas",
|
||||
"platform_no_platform": "Ninguna plataforma",
|
||||
"platform_page": "Página {0}/{1}",
|
||||
@@ -24,7 +25,11 @@
|
||||
"game_count": "{0} ({1} juegos)",
|
||||
"game_filter": "Filtro activo: {0}",
|
||||
"game_search": "Filtrar: {0}",
|
||||
"global_search_title": "Busqueda global: {0}",
|
||||
"global_search_empty_query": "Escribe un nombre para buscar en todas las consolas",
|
||||
"global_search_no_results": "Sin resultados para: {0}",
|
||||
"game_header_name": "Nombre",
|
||||
"game_header_ext": "Ext",
|
||||
"game_header_size": "Tamaño",
|
||||
"history_title": "Descargas ({0})",
|
||||
"history_empty": "No hay descargas en el historial",
|
||||
@@ -42,6 +47,9 @@
|
||||
"free_mode_submitting": "[Modo gratuito] Enviando formulario...",
|
||||
"free_mode_link_found": "[Modo gratuito] Enlace encontrado: {0}...",
|
||||
"free_mode_completed": "[Modo gratuito] Completado: {0}",
|
||||
"free_mode_guest_slots_unavailable": "1fichier: la descarga gratuita como invitado no está disponible temporalmente (todos los cupos están ocupados). Inténtelo de nuevo más tarde.",
|
||||
"free_mode_unavailable_in_app": "1fichier: esta descarga no está disponible en la aplicación en este momento. Inténtelo de nuevo más tarde.",
|
||||
"free_mode_premium_advice": "Para descargar de forma ilimitada, cuando quiera y a máxima velocidad, necesita una cuenta premium o un desbridizador y debe introducir su clave API en RGSX.",
|
||||
"download_status": "{0}: {1}",
|
||||
"download_canceled": "Descarga cancelada por el usuario.",
|
||||
"download_removed_from_queue": "Eliminado de la cola de descarga",
|
||||
@@ -51,8 +59,8 @@
|
||||
"confirm_exit": "¿Salir de la aplicación?",
|
||||
"confirm_exit_with_downloads": "Atención: {0} descarga(s) en curso. ¿Salir de todas formas?",
|
||||
"confirm_clear_history": "¿Vaciar el historial?",
|
||||
"confirm_redownload_cache": "¿Actualizar la lista de juegos?",
|
||||
"popup_redownload_success": "Caché borrada, por favor reinicia la aplicación",
|
||||
"confirm_redownload_cache": "¿Actualizar la lista de juegos?", "gamelist_update_prompt_with_date": "La lista de juegos no se ha actualizado durante más de {0} días (última actualización: {1}). ¿Descargar la última versión?",
|
||||
"gamelist_update_prompt_first_time": "¿Desea descargar la última lista de juegos?", "popup_redownload_success": "Caché borrada, por favor reinicia la aplicación",
|
||||
"popup_no_cache": "No se encontró caché.\nPor favor, reinicia la aplicación para cargar los juegos.",
|
||||
"popup_countdown": "Este mensaje se cerrará en {0} segundo{1}",
|
||||
"language_select_title": "Selección de idioma",
|
||||
@@ -60,6 +68,12 @@
|
||||
"language_changed": "Idioma cambiado a {0}",
|
||||
"menu_controls": "Controles",
|
||||
"menu_remap_controls": "Remapear controles",
|
||||
"menu_nintendo_layout_on": "Diseño de controlador Nintendo",
|
||||
"menu_nintendo_layout_off": "Diseño de controlador Xbox",
|
||||
"instruction_nintendo_layout": "Invierte los controles mostrados para coincidir con el diseño",
|
||||
"controller_style_label": "Estilo de controlador :",
|
||||
"controller_style_nintendo": "Nintendo",
|
||||
"controller_style_xbox": "Xbox",
|
||||
"menu_history": "Historial",
|
||||
"menu_language": "Idioma",
|
||||
"menu_accessibility": "Accesibilidad",
|
||||
@@ -103,6 +117,7 @@
|
||||
"controls_action_clear_history": "Vaciar historial",
|
||||
"controls_action_history": "Historial / Descargas",
|
||||
"controls_action_close_history": "Cerrar Historial",
|
||||
"history_column_folder": "Carpeta",
|
||||
"controls_action_delete": "Eliminar",
|
||||
"controls_action_space": "Espacio",
|
||||
"controls_action_start": "Ayuda / Configuración",
|
||||
@@ -136,6 +151,8 @@
|
||||
"controls_confirm_select": "Confirmar/Seleccionar",
|
||||
"controls_cancel_back": "Cancelar/Volver",
|
||||
"controls_filter_search": "Filtrar/Buscar",
|
||||
"controls_action_edit_search": "Editar busqueda",
|
||||
"controls_action_show_results": "Ver resultados",
|
||||
"network_download_failed": "Error en la descarga tras {0} intentos",
|
||||
"network_api_error": "Error en la solicitud de API, la clave puede ser incorrecta: {0}",
|
||||
"network_download_error": "Error en la descarga {0}: {1}",
|
||||
@@ -183,7 +200,14 @@
|
||||
"status_present": "Presente",
|
||||
"status_missing": "Ausente",
|
||||
"menu_api_keys_status": "Claves API",
|
||||
"menu_connection_status": "Estado de conexión",
|
||||
"api_keys_status_title": "Estado de las claves API",
|
||||
"connection_status_title": "Estado de conexión",
|
||||
"connection_status_category_updates": "Actualización app/lista de juegos",
|
||||
"connection_status_category_sources": "Fuentes de juegos",
|
||||
"connection_status_checking": "Comprobando...",
|
||||
"connection_status_progress": "Comprobando... {done}/{total}",
|
||||
"connection_status_last_check": "Última comprobación: {time}",
|
||||
"menu_games": "Juegos",
|
||||
"api_keys_hint_manage": "Coloca tus claves en {path}",
|
||||
"api_key_empty_suffix": "vacío",
|
||||
@@ -219,11 +243,24 @@
|
||||
"instruction_games_history": "Ver descargas pasadas y su estado",
|
||||
"instruction_games_source_mode": "Cambiar entre lista RGSX o fuente personalizada",
|
||||
"instruction_games_update_cache": "Volver a descargar y refrescar la lista de juegos",
|
||||
"instruction_games_scan_owned": "Escanear las carpetas ROMs y marcar los juegos que ya posees",
|
||||
"instruction_settings_music": "Activar o desactivar música de fondo",
|
||||
"instruction_settings_symlink": "Alternar uso de symlinks en instalaciones",
|
||||
"instruction_settings_auto_extract": "Activar/desactivar extracción automática de archivos después de descargar",
|
||||
"instruction_settings_roms_folder": "Cambiar el directorio de descarga de ROMs por defecto",
|
||||
"instruction_settings_api_keys": "Ver claves API premium detectadas",
|
||||
"instruction_settings_connection_status": "Comprobar acceso a sitios de actualizaciones y fuentes",
|
||||
"instruction_settings_web_service": "Activar/desactivar inicio automático del servicio web",
|
||||
"instruction_settings_custom_dns": "Activar/desactivar DNS personalizado (Cloudflare 1.1.1.1) al inicio",
|
||||
"settings_auto_extract": "Extracción auto de archivos",
|
||||
"settings_auto_extract_enabled": "Activado",
|
||||
"settings_auto_extract_disabled": "Desactivado",
|
||||
"settings_roms_folder": "Carpeta ROMs",
|
||||
"settings_roms_folder_default": "Por defecto",
|
||||
"roms_folder_set": "Carpeta ROMs configurada: {0}",
|
||||
"roms_folder_set_restart": "Carpeta ROMs configurada: {0}\n¡Reinicio necesario para aplicar!",
|
||||
"roms_folder_reset": "Carpeta ROMs restablecida por defecto\n¡Reinicio necesario para aplicar!",
|
||||
"folder_browser_title_roms_root": "Seleccionar carpeta ROMs por defecto",
|
||||
"settings_web_service": "Servicio Web al Inicio",
|
||||
"settings_web_service_enabled": "Activado",
|
||||
"settings_web_service_disabled": "Desactivado",
|
||||
@@ -233,6 +270,9 @@
|
||||
"settings_web_service_success_disabled": "Servicio web desactivado al inicio",
|
||||
"settings_web_service_error": "Error: {0}",
|
||||
"settings_custom_dns": "DNS Personalizado al Inicio",
|
||||
"menu_scan_owned_roms": "Escanear ROMs disponibles",
|
||||
"popup_scan_owned_roms_done": "Escaneo ROM completado: {0} juegos añadidos en {1} plataformas",
|
||||
"popup_scan_owned_roms_error": "Error al escanear ROMs: {0}",
|
||||
"settings_custom_dns_enabled": "Activado",
|
||||
"settings_custom_dns_disabled": "Desactivado",
|
||||
"settings_custom_dns_enabling": "Activando DNS personalizado...",
|
||||
@@ -266,9 +306,12 @@
|
||||
"history_option_scraper": "Obtener metadatos",
|
||||
"history_option_remove_from_queue": "Quitar de la cola",
|
||||
"history_option_cancel_download": "Cancelar descarga",
|
||||
"history_option_pause_download": "Pausar descarga",
|
||||
"history_option_resume_download": "Reanudar descarga",
|
||||
"history_option_delete_game": "Eliminar juego",
|
||||
"history_option_error_info": "Detalles del error",
|
||||
"history_option_retry": "Reintentar descarga",
|
||||
"history_move_action": "Mover",
|
||||
"history_option_back": "Volver",
|
||||
"history_folder_path_label": "Ruta de destino:",
|
||||
"history_scraper_not_implemented": "Scraper aún no implementado",
|
||||
@@ -278,6 +321,8 @@
|
||||
"history_extracted": "Extraído",
|
||||
"history_delete_success": "Juego eliminado con éxito",
|
||||
"history_delete_error": "Error al eliminar juego: {0}",
|
||||
"history_move_success": "{0} archivo(s) movido(s) a: {1}",
|
||||
"history_move_error": "Error al mover los archivos: {0}",
|
||||
"history_error_details_title": "Detalles del error",
|
||||
"history_no_error_message": "No hay mensaje de error disponible",
|
||||
"web_title": "Interfaz Web RGSX",
|
||||
@@ -328,6 +373,9 @@
|
||||
"web_settings_source_mode": "Fuente de juegos",
|
||||
"web_settings_custom_url": "URL personalizada",
|
||||
"web_settings_custom_url_placeholder": "Dejar vacío para /saves/ports/rgsx/games.zip o usar una URL directa como https://ejemplo.com/juegos.zip",
|
||||
"web_settings_auto_extract": "Extraer archivos automáticamente después de descargar",
|
||||
"web_settings_web_service": "Iniciar servicio web al arrancar",
|
||||
"web_settings_custom_dns": "Activar DNS personalizado al arrancar",
|
||||
"web_settings_save": "Guardar configuración",
|
||||
"web_settings_saved": "¡Configuración guardada con éxito!",
|
||||
"web_settings_saved_restart": "¡Configuración guardada con éxito!\\n\\n⚠️ Algunos ajustes requieren reiniciar el servidor:\\n- Carpeta ROMs personalizada\\n- Idioma\\n\\nPor favor, reinicie el servidor web para aplicar estos cambios.",
|
||||
@@ -351,6 +399,7 @@
|
||||
"web_history_status_completed": "Completado",
|
||||
"web_history_status_error": "Error",
|
||||
"web_settings_os": "Sistema operativo",
|
||||
"web_system_info_title": "Información del sistema",
|
||||
"web_settings_platforms_count": "Número de plataformas",
|
||||
"web_settings_show_unsupported": "Mostrar plataformas no compatibles (sistema ausente en es_systems.cfg)",
|
||||
"web_settings_allow_unknown": "Permitir extensiones desconocidas (no mostrar advertencias)",
|
||||
@@ -427,5 +476,24 @@
|
||||
"filter_reset_filters": "Restablecer",
|
||||
"filter_back": "Volver",
|
||||
"filter_active": "Filtro activo",
|
||||
"filter_games_shown": "{0} juego(s) mostrado(s)"
|
||||
"filter_games_shown": "{0} juego(s) mostrado(s)",
|
||||
"platform_folder_config_current": "Configurar carpeta de descarga para {0}\nActual: {1}",
|
||||
"platform_folder_config_default": "Configurar carpeta de descarga para {0}\nUsando ubicación predeterminada",
|
||||
"platform_folder_show_current": "Mostrar ruta actual",
|
||||
"platform_folder_browse": "Examinar",
|
||||
"platform_folder_reset": "Restablecer predeterminado",
|
||||
"platform_folder_set": "Carpeta establecida para {0}: {1}",
|
||||
"platform_folder_default_path": "Por defecto: {0}",
|
||||
"folder_browser_title": "Seleccionar carpeta para {0}",
|
||||
"folder_browser_title_history_move": "Seleccionar carpeta de destino",
|
||||
"folder_browser_parent": "Carpeta superior",
|
||||
"folder_browser_enter": "Entrar",
|
||||
"folder_browser_select": "Seleccionar",
|
||||
"folder_new_folder": "Nueva carpeta",
|
||||
"folder_new_title": "Crear nueva carpeta",
|
||||
"folder_new_confirm": "Crear",
|
||||
"folder_created": "Carpeta creada: {0}",
|
||||
"folder_create_error": "Error al crear: {0}",
|
||||
"controls_action_select_char": "Añadir",
|
||||
"folder_browser_browse": "Explorar"
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
"error_no_internet": "Pas de connexion Internet. Vérifiez votre réseau.",
|
||||
"error_api_key": "Attention il faut renseigner sa clé API (premium only) dans le fichier {0}",
|
||||
"error_invalid_download_data": "Données de téléchargement invalides",
|
||||
"popup_torrent_in_maintenance": "Torrent en maintenance, veuillez patienter",
|
||||
"error_delete_sources": "Erreur lors de la suppression du fichier systems_list.json ou dossiers",
|
||||
"platform_no_platform": "Aucune plateforme",
|
||||
"platform_page": "Page {0}/{1}",
|
||||
@@ -24,7 +25,11 @@
|
||||
"game_count": "{0} ({1} jeux)",
|
||||
"game_filter": "Filtre actif : {0}",
|
||||
"game_search": "Filtrer : {0}",
|
||||
"global_search_title": "Recherche globale : {0}",
|
||||
"global_search_empty_query": "Saisissez un nom pour rechercher dans toutes les consoles",
|
||||
"global_search_no_results": "Aucun resultat pour : {0}",
|
||||
"game_header_name": "Nom",
|
||||
"game_header_ext": "Ext",
|
||||
"game_header_size": "Taille",
|
||||
"history_title": "Téléchargements ({0})",
|
||||
"history_empty": "Aucun téléchargement dans l'historique",
|
||||
@@ -42,6 +47,9 @@
|
||||
"free_mode_submitting": "[Mode gratuit] Soumission formulaire...",
|
||||
"free_mode_link_found": "[Mode gratuit] Lien trouvé: {0}...",
|
||||
"free_mode_completed": "[Mode gratuit] Terminé: {0}",
|
||||
"free_mode_guest_slots_unavailable": "1fichier : le téléchargement gratuit invité est temporairement indisponible (tous les créneaux sont occupés). Réessayez plus tard.",
|
||||
"free_mode_unavailable_in_app": "1fichier : ce téléchargement n'est pas disponible dans l'application pour le moment. Réessayez plus tard.",
|
||||
"free_mode_premium_advice": "Pour télécharger de manière illimitée, quand vous voulez et à pleine vitesse, vous devez obtenir un compte premium ou un débrideur et entrer votre clé API dans RGSX.",
|
||||
"download_status": "{0} : {1}",
|
||||
"download_canceled": "Téléchargement annulé par l'utilisateur.",
|
||||
"download_removed_from_queue": "Retiré de la file de téléchargement",
|
||||
@@ -52,6 +60,8 @@
|
||||
"confirm_exit_with_downloads": "Attention : {0} téléchargement(s) en cours. Quitter quand même ?",
|
||||
"confirm_clear_history": "Vider l'historique ?",
|
||||
"confirm_redownload_cache": "Mettre à jour la liste des jeux ?",
|
||||
"gamelist_update_prompt_with_date": "La liste des jeux n'a pas été mise à jour depuis plus de {0} jours (dernière mise à jour : {1}). Télécharger la dernière version ?",
|
||||
"gamelist_update_prompt_first_time": "Souhaitez-vous télécharger la dernière liste des jeux ?",
|
||||
"popup_redownload_success": "Le cache a été effacé, merci de relancer l'application",
|
||||
"popup_no_cache": "Aucun cache trouvé.\nVeuillez redémarrer l'application pour charger les jeux.",
|
||||
"popup_countdown": "Ce message se fermera dans {0} seconde{1}",
|
||||
@@ -104,6 +114,7 @@
|
||||
"controls_action_queue": "Mettre en file d'attente",
|
||||
"controls_action_history": "Historique / Téléchargements",
|
||||
"controls_action_close_history": "Fermer l'historique",
|
||||
"history_column_folder": "Dossier",
|
||||
"controls_action_delete": "Supprimer",
|
||||
"controls_action_space": "Espace",
|
||||
"controls_action_start": "Aide / Réglages",
|
||||
@@ -136,6 +147,8 @@
|
||||
"controls_confirm_select": "Confirmer/Sélectionner",
|
||||
"controls_cancel_back": "Annuler/Retour",
|
||||
"controls_filter_search": "Filtrer/Rechercher",
|
||||
"controls_action_edit_search": "Modifier recherche",
|
||||
"controls_action_show_results": "Voir resultats",
|
||||
"network_download_failed": "Échec du téléchargement après {0} tentatives",
|
||||
"network_api_error": "Erreur lors de la requête API, la clé est peut-être incorrecte: {0}",
|
||||
"network_download_error": "Erreur téléchargement {0}: {1}",
|
||||
@@ -183,7 +196,14 @@
|
||||
"status_present": "Présente",
|
||||
"status_missing": "Absente",
|
||||
"menu_api_keys_status": "Clés API",
|
||||
"menu_connection_status": "État de connexion",
|
||||
"api_keys_status_title": "Statut des clés API",
|
||||
"connection_status_title": "État de connexion",
|
||||
"connection_status_category_updates": "Mise à jour App/Liste de jeux",
|
||||
"connection_status_category_sources": "Sources de jeux",
|
||||
"connection_status_checking": "Vérification...",
|
||||
"connection_status_progress": "Vérification... {done}/{total}",
|
||||
"connection_status_last_check": "Dernière vérif : {time}",
|
||||
"menu_games": "Jeux",
|
||||
"api_keys_hint_manage": "Placez vos clés dans {path}",
|
||||
"api_key_empty_suffix": "vide",
|
||||
@@ -203,6 +223,12 @@
|
||||
"instruction_quit_app": "Quitter l'application RGSX",
|
||||
"instruction_quit_restart": "Redémarrer l'application RGSX",
|
||||
"instruction_controls_help": "Afficher la référence complète manette & clavier",
|
||||
"menu_nintendo_layout_on": "Disposition manette Nintendo",
|
||||
"menu_nintendo_layout_off": "Disposition manette Xbox",
|
||||
"instruction_nintendo_layout": "Inverse l'affichage des contrôles pour correspondre au layout",
|
||||
"controller_style_label": "Style manette :",
|
||||
"controller_style_nintendo": "Nintendo",
|
||||
"controller_style_xbox": "Xbox",
|
||||
"instruction_controls_remap": "Modifier l'association boutons / touches",
|
||||
"instruction_generic_back": "Revenir au menu précédent",
|
||||
"instruction_display_layout": "Changer les dimensions de la grille",
|
||||
@@ -219,11 +245,24 @@
|
||||
"instruction_games_history": "Lister les téléchargements passés et leur statut",
|
||||
"instruction_games_source_mode": "Basculer entre liste RGSX ou source personnalisée",
|
||||
"instruction_games_update_cache": "Retélécharger & rafraîchir la liste des jeux",
|
||||
"instruction_games_scan_owned": "Scanner les dossiers ROMs et marquer les jeux déjà possédés",
|
||||
"instruction_settings_music": "Activer ou désactiver la lecture musicale",
|
||||
"instruction_settings_symlink": "Basculer l'utilisation de symlinks pour l'installation",
|
||||
"instruction_settings_auto_extract": "Activer/désactiver l'extraction automatique des archives après téléchargement",
|
||||
"instruction_settings_roms_folder": "Changer le répertoire de téléchargement des ROMs par défaut",
|
||||
"instruction_settings_api_keys": "Voir les clés API détectées des services premium",
|
||||
"instruction_settings_connection_status": "Vérifier l'accès aux sites d'update et de sources",
|
||||
"instruction_settings_web_service": "Activer/désactiver le démarrage automatique du service web",
|
||||
"instruction_settings_custom_dns": "Activer/désactiver les DNS personnalisés (Cloudflare 1.1.1.1) au démarrage",
|
||||
"settings_auto_extract": "Extraction auto des archives",
|
||||
"settings_auto_extract_enabled": "Activé",
|
||||
"settings_auto_extract_disabled": "Désactivé",
|
||||
"settings_roms_folder": "Dossier ROMs",
|
||||
"settings_roms_folder_default": "Par défaut",
|
||||
"roms_folder_set": "Dossier ROMs défini: {0}",
|
||||
"roms_folder_set_restart": "Dossier ROMs défini: {0}\nRedémarrage nécessaire pour appliquer!",
|
||||
"roms_folder_reset": "Dossier ROMs réinitialisé par défaut\nRedémarrage nécessaire pour appliquer!",
|
||||
"folder_browser_title_roms_root": "Sélectionner le dossier ROMs par défaut",
|
||||
"settings_web_service": "Service Web au démarrage",
|
||||
"settings_web_service_enabled": "Activé",
|
||||
"settings_web_service_disabled": "Désactivé",
|
||||
@@ -264,9 +303,17 @@
|
||||
"history_option_extract_archive": "Extraire l'archive",
|
||||
"history_option_open_file": "Ouvrir le fichier",
|
||||
"history_option_scraper": "Récupérer métadonnées",
|
||||
"history_option_remove_from_queue": "Retirer de la file d'attente",
|
||||
"history_option_cancel_download": "Annuler le téléchargement",
|
||||
"history_option_pause_download": "Mettre en pause",
|
||||
"history_option_resume_download": "Reprendre le téléchargement",
|
||||
"history_option_delete_game": "Supprimer le jeu",
|
||||
"history_option_error_info": "Détails de l'erreur",
|
||||
"history_option_retry": "Retenter le téléchargement",
|
||||
"history_move_action": "Déplacer",
|
||||
"menu_scan_owned_roms": "Scanner les ROMs présentes",
|
||||
"popup_scan_owned_roms_done": "Scan ROMs terminé : {0} jeux ajoutés sur {1} plateformes",
|
||||
"popup_scan_owned_roms_error": "Erreur scan ROMs : {0}",
|
||||
"history_option_back": "Retour",
|
||||
"history_folder_path_label": "Chemin de destination :",
|
||||
"history_scraper_not_implemented": "Scraper pas encore implémenté",
|
||||
@@ -276,6 +323,8 @@
|
||||
"history_extracted": "Extrait",
|
||||
"history_delete_success": "Jeu supprimé avec succès",
|
||||
"history_delete_error": "Erreur lors de la suppression du jeu : {0}",
|
||||
"history_move_success": "{0} fichier(s) déplacé(s) vers : {1}",
|
||||
"history_move_error": "Erreur lors du déplacement : {0}",
|
||||
"history_error_details_title": "Détails de l'erreur",
|
||||
"history_no_error_message": "Aucun message d'erreur disponible",
|
||||
"web_title": "Interface Web RGSX",
|
||||
@@ -326,6 +375,9 @@
|
||||
"web_settings_source_mode": "Source des jeux",
|
||||
"web_settings_custom_url": "URL personnalisée",
|
||||
"web_settings_custom_url_placeholder": "Laisser vide pour /saves/ports/rgsx/games.zip ou utiliser une URL directe comme https://exemple.com/jeux.zip",
|
||||
"web_settings_auto_extract": "Extraction auto des archives après téléchargement",
|
||||
"web_settings_web_service": "Lancer le service web au démarrage",
|
||||
"web_settings_custom_dns": "Activer le DNS personnalisé au démarrage",
|
||||
"web_settings_save": "Enregistrer les paramètres",
|
||||
"web_settings_saved": "Paramètres enregistrés avec succès !",
|
||||
"web_settings_saved_restart": "Paramètres enregistrés avec succès !\\n\\n⚠️ Certains paramètres nécessitent un redémarrage du serveur :\\n- Dossier ROMs personnalisé\\n- Langue\\n\\nVeuillez redémarrer le serveur web pour appliquer ces changements.",
|
||||
@@ -349,6 +401,7 @@
|
||||
"web_history_status_completed": "Terminé",
|
||||
"web_history_status_error": "Erreur",
|
||||
"web_settings_os": "Système d'exploitation",
|
||||
"web_system_info_title": "Informations système",
|
||||
"web_settings_platforms_count": "Nombre de plateformes",
|
||||
"web_settings_show_unsupported": "Afficher les systèmes non supportés (absents de es_systems.cfg)",
|
||||
"web_settings_allow_unknown": "Autoriser les extensions inconnues (ne pas afficher d'avertissement)",
|
||||
@@ -425,5 +478,24 @@
|
||||
"filter_reset_filters": "Réinitialiser",
|
||||
"filter_back": "Retour",
|
||||
"filter_active": "Filtre actif",
|
||||
"filter_games_shown": "{0} jeu(x) affiché(s)"
|
||||
"filter_games_shown": "{0} jeu(x) affiché(s)",
|
||||
"platform_folder_config_current": "Configurer le dossier de téléchargement pour {0}\nActuel: {1}",
|
||||
"platform_folder_config_default": "Configurer le dossier de téléchargement pour {0}\nUtilise le dossier par défaut",
|
||||
"platform_folder_show_current": "Afficher le chemin actuel",
|
||||
"platform_folder_browse": "Parcourir",
|
||||
"platform_folder_reset": "Rétablir par défaut",
|
||||
"platform_folder_set": "Dossier défini pour {0}: {1}",
|
||||
"platform_folder_default_path": "Par défaut: {0}",
|
||||
"folder_browser_title": "Sélectionner le dossier pour {0}",
|
||||
"folder_browser_title_history_move": "Sélectionner le dossier de destination",
|
||||
"folder_browser_parent": "Dossier parent",
|
||||
"folder_browser_enter": "Entrer",
|
||||
"folder_browser_select": "Valider",
|
||||
"folder_new_folder": "Nouveau dossier",
|
||||
"folder_new_title": "Créer un nouveau dossier",
|
||||
"folder_new_confirm": "Créer",
|
||||
"folder_created": "Dossier créé: {0}",
|
||||
"folder_create_error": "Erreur lors de la création: {0}",
|
||||
"controls_action_select_char": "Ajouter",
|
||||
"folder_browser_browse": "Parcourir"
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
"error_no_internet": "Nessuna connessione Internet. Controlla la rete.",
|
||||
"error_api_key": "Inserisci la tua API key (solo premium) nel file {0}",
|
||||
"error_invalid_download_data": "Dati di download non validi",
|
||||
"popup_torrent_in_maintenance": "Torrent in manutenzione, attendere prego",
|
||||
"error_delete_sources": "Errore nell'eliminazione del file systems_list.json o delle cartelle",
|
||||
"platform_no_platform": "Nessuna piattaforma",
|
||||
"platform_page": "Pagina {0}/{1}",
|
||||
@@ -24,7 +25,11 @@
|
||||
"game_count": "{0} ({1} giochi)",
|
||||
"game_filter": "Filtro attivo: {0}",
|
||||
"game_search": "Filtro: {0}",
|
||||
"global_search_title": "Ricerca globale: {0}",
|
||||
"global_search_empty_query": "Digita un nome per cercare in tutte le console",
|
||||
"global_search_no_results": "Nessun risultato per: {0}",
|
||||
"game_header_name": "Nome",
|
||||
"game_header_ext": "Ext",
|
||||
"game_header_size": "Dimensione",
|
||||
"history_title": "Download ({0})",
|
||||
"history_empty": "Nessun download nella cronologia",
|
||||
@@ -42,6 +47,9 @@
|
||||
"free_mode_submitting": "[Modalità gratuita] Invio modulo...",
|
||||
"free_mode_link_found": "[Modalità gratuita] Link trovato: {0}...",
|
||||
"free_mode_completed": "[Modalità gratuita] Completato: {0}",
|
||||
"free_mode_guest_slots_unavailable": "1fichier: il download gratuito come ospite non è temporaneamente disponibile (tutti gli slot sono occupati). Riprova più tardi.",
|
||||
"free_mode_unavailable_in_app": "1fichier: questo download non è disponibile nell'applicazione in questo momento. Riprova più tardi.",
|
||||
"free_mode_premium_advice": "Per scaricare senza limiti, quando vuoi e alla massima velocità, hai bisogno di un account premium o di un servizio debrid e devi inserire la sua chiave API in RGSX.",
|
||||
"download_status": "{0}: {1}",
|
||||
"download_canceled": "Download annullato dall'utente.",
|
||||
"download_removed_from_queue": "Rimosso dalla coda di download",
|
||||
@@ -51,8 +59,8 @@
|
||||
"confirm_exit": "Uscire dall'applicazione?",
|
||||
"confirm_exit_with_downloads": "Attenzione: {0} download in corso. Uscire comunque?",
|
||||
"confirm_clear_history": "Cancellare la cronologia?",
|
||||
"confirm_redownload_cache": "Aggiornare l'elenco dei giochi?",
|
||||
"popup_redownload_success": "Cache pulita, riavvia l'applicazione",
|
||||
"confirm_redownload_cache": "Aggiornare l'elenco dei giochi?", "gamelist_update_prompt_with_date": "L'elenco dei giochi non è stato aggiornato da più di {0} giorni (ultimo aggiornamento: {1}). Scaricare l'ultima versione?",
|
||||
"gamelist_update_prompt_first_time": "Vuoi scaricare l'ultimo elenco dei giochi?", "popup_redownload_success": "Cache pulita, riavvia l'applicazione",
|
||||
"popup_no_cache": "Nessuna cache trovata.\nRiavvia l'applicazione per caricare i giochi.",
|
||||
"popup_countdown": "Questo messaggio si chiuderà tra {0} secondo{1}",
|
||||
"language_select_title": "Selezione lingua",
|
||||
@@ -60,6 +68,12 @@
|
||||
"language_changed": "Lingua cambiata in {0}",
|
||||
"menu_controls": "Controlli",
|
||||
"menu_remap_controls": "Rimappa controlli",
|
||||
"menu_nintendo_layout_on": "Layout controller Nintendo",
|
||||
"menu_nintendo_layout_off": "Layout controller Xbox",
|
||||
"instruction_nintendo_layout": "Inverti i controlli visualizzati per corrispondere al layout",
|
||||
"controller_style_label": "Stile controller :",
|
||||
"controller_style_nintendo": "Nintendo",
|
||||
"controller_style_xbox": "Xbox",
|
||||
"menu_history": "Cronologia",
|
||||
"menu_language": "Lingua",
|
||||
"menu_accessibility": "Accessibilità",
|
||||
@@ -101,6 +115,7 @@
|
||||
"controls_action_clear_history": "Cancella cronologia",
|
||||
"controls_action_history": "Cronologia / Downloads",
|
||||
"controls_action_close_history": "Chiudi Cronologia",
|
||||
"history_column_folder": "Cartella",
|
||||
"controls_action_delete": "Elimina",
|
||||
"controls_action_space": "Spazio",
|
||||
"controls_action_start": "Aiuto / Impostazioni",
|
||||
@@ -158,6 +173,8 @@
|
||||
"controls_confirm_select": "Conferma/Seleziona",
|
||||
"controls_cancel_back": "Annulla/Indietro",
|
||||
"controls_filter_search": "Filtro/Ricerca",
|
||||
"controls_action_edit_search": "Modifica ricerca",
|
||||
"controls_action_show_results": "Mostra risultati",
|
||||
"games_source_rgsx": "RGSX",
|
||||
"sources_mode_rgsx_select_info": "RGSX: aggiorna l'elenco dei giochi",
|
||||
"games_source_custom": "Personalizzato",
|
||||
@@ -178,7 +195,14 @@
|
||||
"status_present": "Presente",
|
||||
"status_missing": "Assente",
|
||||
"menu_api_keys_status": "Chiavi API",
|
||||
"menu_connection_status": "Stato connessione",
|
||||
"api_keys_status_title": "Stato delle chiavi API",
|
||||
"connection_status_title": "Stato connessione",
|
||||
"connection_status_category_updates": "Aggiornamento app/lista giochi",
|
||||
"connection_status_category_sources": "Sorgenti giochi",
|
||||
"connection_status_checking": "Verifica in corso...",
|
||||
"connection_status_progress": "Verifica in corso... {done}/{total}",
|
||||
"connection_status_last_check": "Ultimo controllo: {time}",
|
||||
"menu_games": "Giochi",
|
||||
"api_keys_hint_manage": "Metti le tue chiavi in {path}",
|
||||
"api_key_empty_suffix": "vuoto",
|
||||
@@ -214,11 +238,24 @@
|
||||
"instruction_games_history": "Elencare download passati e stato",
|
||||
"instruction_games_source_mode": "Passare tra elenco RGSX o sorgente personalizzata",
|
||||
"instruction_games_update_cache": "Riscaria e aggiorna l'elenco dei giochi",
|
||||
"instruction_games_scan_owned": "Scansiona le cartelle ROMs e segna i giochi gia posseduti",
|
||||
"instruction_settings_music": "Abilitare o disabilitare musica di sottofondo",
|
||||
"instruction_settings_symlink": "Abilitare/disabilitare uso symlink per installazioni",
|
||||
"instruction_settings_auto_extract": "Attivare/disattivare estrazione automatica archivi dopo il download",
|
||||
"instruction_settings_roms_folder": "Cambiare la directory di download ROMs predefinita",
|
||||
"instruction_settings_api_keys": "Mostrare chiavi API premium rilevate",
|
||||
"instruction_settings_connection_status": "Verifica accesso ai siti di aggiornamento e sorgenti",
|
||||
"instruction_settings_web_service": "Attivare/disattivare avvio automatico servizio web all'avvio",
|
||||
"instruction_settings_custom_dns": "Attivare/disattivare DNS personalizzato (Cloudflare 1.1.1.1) all'avvio",
|
||||
"settings_auto_extract": "Estrazione auto archivi",
|
||||
"settings_auto_extract_enabled": "Attivato",
|
||||
"settings_auto_extract_disabled": "Disattivato",
|
||||
"settings_roms_folder": "Cartella ROMs",
|
||||
"settings_roms_folder_default": "Predefinita",
|
||||
"roms_folder_set": "Cartella ROMs impostata: {0}",
|
||||
"roms_folder_set_restart": "Cartella ROMs impostata: {0}\nRiavvio necessario per applicare!",
|
||||
"roms_folder_reset": "Cartella ROMs ripristinata predefinita\nRiavvio necessario per applicare!",
|
||||
"folder_browser_title_roms_root": "Seleziona cartella ROMs predefinita",
|
||||
"settings_web_service": "Servizio Web all'Avvio",
|
||||
"settings_web_service_enabled": "Abilitato",
|
||||
"settings_web_service_disabled": "Disabilitato",
|
||||
@@ -233,6 +270,9 @@
|
||||
"settings_custom_dns_enabling": "Abilitazione DNS personalizzato...",
|
||||
"settings_custom_dns_disabling": "Disabilitazione DNS personalizzato...",
|
||||
"settings_custom_dns_success_enabled": "DNS personalizzato abilitato all'avvio (1.1.1.1)",
|
||||
"menu_scan_owned_roms": "Scansiona ROM presenti",
|
||||
"popup_scan_owned_roms_done": "Scansione ROM completata: {0} giochi aggiunti su {1} piattaforme",
|
||||
"popup_scan_owned_roms_error": "Errore scansione ROM: {0}",
|
||||
"settings_custom_dns_success_disabled": "DNS personalizzato disabilitato all'avvio",
|
||||
"controls_desc_confirm": "Confermare (es. A/Croce)",
|
||||
"controls_desc_cancel": "Annullare/Indietro (es. B/Cerchio)",
|
||||
@@ -259,9 +299,14 @@
|
||||
"history_option_extract_archive": "Estrai archivio",
|
||||
"history_option_open_file": "Apri file",
|
||||
"history_option_scraper": "Scraper metadati",
|
||||
"history_option_remove_from_queue": "Rimuovi dalla coda",
|
||||
"history_option_cancel_download": "Annulla download",
|
||||
"history_option_pause_download": "Pausa download",
|
||||
"history_option_resume_download": "Riprendi download",
|
||||
"history_option_delete_game": "Elimina gioco",
|
||||
"history_option_error_info": "Dettagli errore",
|
||||
"history_option_retry": "Riprova download",
|
||||
"history_move_action": "Sposta",
|
||||
"history_option_back": "Indietro",
|
||||
"history_folder_path_label": "Percorso destinazione:",
|
||||
"history_scraper_not_implemented": "Scraper non ancora implementato",
|
||||
@@ -271,6 +316,8 @@
|
||||
"history_extracted": "Estratto",
|
||||
"history_delete_success": "Gioco eliminato con successo",
|
||||
"history_delete_error": "Errore durante l'eliminazione del gioco: {0}",
|
||||
"history_move_success": "{0} file spostato/i in: {1}",
|
||||
"history_move_error": "Errore durante lo spostamento: {0}",
|
||||
"history_error_details_title": "Dettagli errore",
|
||||
"history_no_error_message": "Nessun messaggio di errore disponibile",
|
||||
"web_title": "Interfaccia Web RGSX",
|
||||
@@ -321,6 +368,9 @@
|
||||
"web_settings_source_mode": "Fonte giochi",
|
||||
"web_settings_custom_url": "URL personalizzato",
|
||||
"web_settings_custom_url_placeholder": " Lasciare vuoto per /saves/ports/rgsx/games.zip o usare una URL diretta come https://esempio.com/giochi.zip",
|
||||
"web_settings_auto_extract": "Estrai automaticamente gli archivi dopo il download",
|
||||
"web_settings_web_service": "Avvia servizio web all'avvio",
|
||||
"web_settings_custom_dns": "Abilita DNS personalizzato all'avvio",
|
||||
"web_settings_save": "Salva impostazioni",
|
||||
"web_settings_saved": "Impostazioni salvate con successo!",
|
||||
"web_settings_saved_restart": "Impostazioni salvate con successo!\\n\\n⚠️ Alcune impostazioni richiedono il riavvio del server:\\n- Cartella ROMs personalizzata\\n- Lingua\\n\\nRiavviare il server web per applicare queste modifiche.",
|
||||
@@ -344,6 +394,7 @@
|
||||
"web_history_status_completed": "Completato",
|
||||
"web_history_status_error": "Errore",
|
||||
"web_settings_os": "Sistema operativo",
|
||||
"web_system_info_title": "Informazioni di sistema",
|
||||
"web_settings_platforms_count": "Numero di piattaforme",
|
||||
"web_settings_show_unsupported": "Mostra piattaforme non supportate (sistema assente in es_systems.cfg)",
|
||||
"web_settings_allow_unknown": "Consenti estensioni sconosciute (non mostrare avvisi)",
|
||||
@@ -423,5 +474,24 @@
|
||||
"filter_reset_filters": "Reimposta",
|
||||
"filter_back": "Indietro",
|
||||
"filter_active": "Filtro attivo",
|
||||
"filter_games_shown": "{0} gioco/i mostrato/i"
|
||||
"filter_games_shown": "{0} gioco/i mostrato/i",
|
||||
"platform_folder_config_current": "Configura cartella download per {0}\nAttuale: {1}",
|
||||
"platform_folder_config_default": "Configura cartella download per {0}\nUsando posizione predefinita",
|
||||
"platform_folder_show_current": "Mostra percorso attuale",
|
||||
"platform_folder_browse": "Sfoglia",
|
||||
"platform_folder_reset": "Ripristina predefinito",
|
||||
"platform_folder_set": "Cartella impostata per {0}: {1}",
|
||||
"platform_folder_default_path": "Predefinito: {0}",
|
||||
"folder_browser_title": "Seleziona cartella per {0}",
|
||||
"folder_browser_title_history_move": "Seleziona cartella di destinazione",
|
||||
"folder_browser_parent": "Cartella superiore",
|
||||
"folder_browser_enter": "Entra",
|
||||
"folder_browser_select": "Seleziona",
|
||||
"folder_new_folder": "Nuova cartella",
|
||||
"folder_new_title": "Crea nuova cartella",
|
||||
"folder_new_confirm": "Crea",
|
||||
"folder_created": "Cartella creata: {0}",
|
||||
"folder_create_error": "Errore nella creazione: {0}",
|
||||
"controls_action_select_char": "Aggiungi",
|
||||
"folder_browser_browse": "Sfoglia"
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
"error_no_internet": "Sem conexão com a Internet. Verifique sua rede.",
|
||||
"error_api_key": "Insira sua chave API (somente premium) no arquivo {0}",
|
||||
"error_invalid_download_data": "Dados de download inválidos",
|
||||
"popup_torrent_in_maintenance": "Torrent em manutenção, aguarde",
|
||||
"error_delete_sources": "Erro ao deletar arquivo sources.json ou pastas",
|
||||
"platform_no_platform": "Sem plataforma",
|
||||
"platform_page": "Página {0}/{1}",
|
||||
@@ -24,7 +25,11 @@
|
||||
"game_count": "{0} ({1} jogos)",
|
||||
"game_filter": "Filtro ativo: {0}",
|
||||
"game_search": "Filtro: {0}",
|
||||
"global_search_title": "Busca global: {0}",
|
||||
"global_search_empty_query": "Digite um nome para buscar em todos os consoles",
|
||||
"global_search_no_results": "Nenhum resultado para: {0}",
|
||||
"game_header_name": "Nome",
|
||||
"game_header_ext": "Ext",
|
||||
"game_header_size": "Tamanho",
|
||||
"history_title": "Downloads ({0})",
|
||||
"history_empty": "Nenhum download no histórico",
|
||||
@@ -42,6 +47,9 @@
|
||||
"free_mode_submitting": "[Modo gratuito] Enviando formulário...",
|
||||
"free_mode_link_found": "[Modo gratuito] Link encontrado: {0}...",
|
||||
"free_mode_completed": "[Modo gratuito] Concluído: {0}",
|
||||
"free_mode_guest_slots_unavailable": "1fichier: o download gratuito como convidado está temporariamente indisponível (todos os slots estão ocupados). Tente novamente mais tarde.",
|
||||
"free_mode_unavailable_in_app": "1fichier: este download não está disponível no aplicativo no momento. Tente novamente mais tarde.",
|
||||
"free_mode_premium_advice": "Para baixar sem limites, quando quiser e em velocidade máxima, você precisa de uma conta premium ou de um serviço debrid e deve inserir a chave API no RGSX.",
|
||||
"download_status": "{0}: {1}",
|
||||
"download_canceled": "Download cancelado pelo usuário.",
|
||||
"download_removed_from_queue": "Removido da fila de download",
|
||||
@@ -52,6 +60,8 @@
|
||||
"confirm_exit_with_downloads": "Atenção: {0} download(s) em andamento. Sair mesmo assim?",
|
||||
"confirm_clear_history": "Limpar histórico?",
|
||||
"confirm_redownload_cache": "Atualizar lista de jogos?",
|
||||
"gamelist_update_prompt_with_date": "A lista de jogos não foi atualizada há mais de {0} dias (última atualização: {1}). Baixar a versão mais recente?",
|
||||
"gamelist_update_prompt_first_time": "Gostaria de baixar a última lista de jogos?",
|
||||
"popup_redownload_success": "Cache limpo, reinicie a aplicação",
|
||||
"popup_no_cache": "Nenhum cache encontrado.\nReinicie a aplicação para carregar os jogos.",
|
||||
"popup_countdown": "Esta mensagem fechará em {0} segundo{1}",
|
||||
@@ -60,6 +70,12 @@
|
||||
"language_changed": "Idioma alterado para {0}",
|
||||
"menu_controls": "Controles",
|
||||
"menu_remap_controls": "Remapear controles",
|
||||
"menu_nintendo_layout_on": "Layout do controle Nintendo",
|
||||
"menu_nintendo_layout_off": "Layout do controle Xbox",
|
||||
"instruction_nintendo_layout": "Inverte os controles exibidos para corresponder ao layout",
|
||||
"controller_style_label": "Estilo do controle :",
|
||||
"controller_style_nintendo": "Nintendo",
|
||||
"controller_style_xbox": "Xbox",
|
||||
"menu_history": "Histórico",
|
||||
"menu_language": "Idioma",
|
||||
"menu_accessibility": "Acessibilidade",
|
||||
@@ -103,6 +119,7 @@
|
||||
"controls_action_clear_history": "Limpar histórico",
|
||||
"controls_action_history": "Histórico / Downloads",
|
||||
"controls_action_close_history": "Fechar Histórico",
|
||||
"history_column_folder": "Pasta",
|
||||
"controls_action_delete": "Deletar",
|
||||
"controls_action_space": "Espaço",
|
||||
"controls_action_start": "Ajuda / Configurações",
|
||||
@@ -159,6 +176,8 @@
|
||||
"controls_confirm_select": "Confirmar/Selecionar",
|
||||
"controls_cancel_back": "Cancelar/Voltar",
|
||||
"controls_filter_search": "Filtrar/Buscar",
|
||||
"controls_action_edit_search": "Editar busca",
|
||||
"controls_action_show_results": "Ver resultados",
|
||||
"symlink_option_enabled": "Opção de symlink ativada",
|
||||
"symlink_option_disabled": "Opção de symlink desativada",
|
||||
"menu_games_source_prefix": "Fonte de jogos",
|
||||
@@ -182,7 +201,14 @@
|
||||
"status_present": "Presente",
|
||||
"status_missing": "Ausente",
|
||||
"menu_api_keys_status": "Chaves API",
|
||||
"menu_connection_status": "Estado da conexão",
|
||||
"api_keys_status_title": "Status das chaves API",
|
||||
"connection_status_title": "Estado da conexão",
|
||||
"connection_status_category_updates": "Atualização do app/lista de jogos",
|
||||
"connection_status_category_sources": "Fontes de jogos",
|
||||
"connection_status_checking": "Verificando...",
|
||||
"connection_status_progress": "Verificando... {done}/{total}",
|
||||
"connection_status_last_check": "Última verificação: {time}",
|
||||
"menu_games": "Jogos",
|
||||
"api_keys_hint_manage": "Coloque suas chaves em {path}",
|
||||
"api_key_empty_suffix": "vazio",
|
||||
@@ -218,11 +244,24 @@
|
||||
"instruction_games_history": "Listar downloads anteriores e status",
|
||||
"instruction_games_source_mode": "Alternar entre lista RGSX ou fonte personalizada",
|
||||
"instruction_games_update_cache": "Baixar novamente e atualizar a lista de jogos",
|
||||
"instruction_games_scan_owned": "Verificar as pastas ROMs e marcar os jogos ja existentes",
|
||||
"instruction_settings_music": "Ativar ou desativar música de fundo",
|
||||
"instruction_settings_symlink": "Ativar/desativar uso de symlinks para instalações",
|
||||
"instruction_settings_auto_extract": "Ativar/desativar extração automática de arquivos após download",
|
||||
"instruction_settings_roms_folder": "Alterar o diretório de download de ROMs padrão",
|
||||
"instruction_settings_api_keys": "Ver chaves API premium detectadas",
|
||||
"instruction_settings_connection_status": "Verificar acesso a sites de atualização e fontes",
|
||||
"instruction_settings_web_service": "Ativar/desativar início automático do serviço web na inicialização",
|
||||
"instruction_settings_custom_dns": "Ativar/desativar DNS personalizado (Cloudflare 1.1.1.1) na inicialização",
|
||||
"settings_auto_extract": "Extração auto de arquivos",
|
||||
"settings_auto_extract_enabled": "Ativado",
|
||||
"settings_auto_extract_disabled": "Desativado",
|
||||
"settings_roms_folder": "Pasta ROMs",
|
||||
"settings_roms_folder_default": "Padrão",
|
||||
"roms_folder_set": "Pasta ROMs definida: {0}",
|
||||
"roms_folder_set_restart": "Pasta ROMs definida: {0}\nReinício necessário para aplicar!",
|
||||
"roms_folder_reset": "Pasta ROMs redefinida para padrão\nReinício necessário para aplicar!",
|
||||
"folder_browser_title_roms_root": "Selecionar pasta ROMs padrão",
|
||||
"settings_web_service": "Serviço Web na Inicialização",
|
||||
"settings_web_service_enabled": "Ativado",
|
||||
"settings_web_service_disabled": "Desativado",
|
||||
@@ -231,6 +270,9 @@
|
||||
"settings_web_service_success_enabled": "Serviço web ativado na inicialização",
|
||||
"settings_web_service_success_disabled": "Serviço web desativado na inicialização",
|
||||
"settings_web_service_error": "Erro: {0}",
|
||||
"menu_scan_owned_roms": "Verificar ROMs existentes",
|
||||
"popup_scan_owned_roms_done": "Verificacao de ROMs concluida: {0} jogos adicionados em {1} plataformas",
|
||||
"popup_scan_owned_roms_error": "Erro ao verificar ROMs: {0}",
|
||||
"settings_custom_dns": "DNS Personalizado na Inicialização",
|
||||
"settings_custom_dns_enabled": "Ativado",
|
||||
"settings_custom_dns_disabled": "Desativado",
|
||||
@@ -265,9 +307,12 @@
|
||||
"history_option_scraper": "Obter metadados",
|
||||
"history_option_remove_from_queue": "Remover da fila",
|
||||
"history_option_cancel_download": "Cancelar download",
|
||||
"history_option_pause_download": "Pausar download",
|
||||
"history_option_resume_download": "Retomar download",
|
||||
"history_option_delete_game": "Excluir jogo",
|
||||
"history_option_error_info": "Detalhes do erro",
|
||||
"history_option_retry": "Tentar novamente",
|
||||
"history_move_action": "Mover",
|
||||
"history_option_back": "Voltar",
|
||||
"history_folder_path_label": "Caminho de destino:",
|
||||
"history_scraper_not_implemented": "Scraper ainda não implementado",
|
||||
@@ -277,6 +322,8 @@
|
||||
"history_extracted": "Extraído",
|
||||
"history_delete_success": "Jogo excluído com sucesso",
|
||||
"history_delete_error": "Erro ao excluir jogo: {0}",
|
||||
"history_move_success": "{0} arquivo(s) movido(s) para: {1}",
|
||||
"history_move_error": "Erro ao mover os arquivos: {0}",
|
||||
"history_error_details_title": "Detalhes do erro",
|
||||
"history_no_error_message": "Nenhuma mensagem de erro disponível",
|
||||
"web_title": "Interface Web RGSX",
|
||||
@@ -327,6 +374,9 @@
|
||||
"web_settings_source_mode": "Fonte de jogos",
|
||||
"web_settings_custom_url": "URL personalizada",
|
||||
"web_settings_custom_url_placeholder": "Deixar vazio para /saves/ports/rgsx/games.zip ou usar uma URL direta como https://example.com/games.zip",
|
||||
"web_settings_auto_extract": "Extrair arquivos automaticamente após o download",
|
||||
"web_settings_web_service": "Iniciar serviço web na inicialização",
|
||||
"web_settings_custom_dns": "Ativar DNS personalizado na inicialização",
|
||||
"web_settings_save": "Salvar configurações",
|
||||
"web_settings_saved": "Configurações salvas com sucesso!",
|
||||
"web_settings_saved_restart": "Configurações salvas com sucesso!\\n\\n⚠️ Algumas configurações exigem reiniciar o servidor:\\n- Pasta ROMs personalizada\\n- Idioma\\n\\nPor favor, reinicie o servidor web para aplicar essas alterações.",
|
||||
@@ -350,6 +400,7 @@
|
||||
"web_history_status_completed": "Concluído",
|
||||
"web_history_status_error": "Erro",
|
||||
"web_settings_os": "Sistema operacional",
|
||||
"web_system_info_title": "Informações do sistema",
|
||||
"web_settings_platforms_count": "Número de plataformas",
|
||||
"web_settings_show_unsupported": "Mostrar plataformas não suportadas (sistema ausente em es_systems.cfg)",
|
||||
"web_settings_allow_unknown": "Permitir extensões desconhecidas (não mostrar avisos)",
|
||||
@@ -427,5 +478,24 @@
|
||||
"filter_reset_filters": "Redefinir",
|
||||
"filter_back": "Voltar",
|
||||
"filter_active": "Filtro ativo",
|
||||
"filter_games_shown": "{0} jogo(s) exibido(s)"
|
||||
"filter_games_shown": "{0} jogo(s) exibido(s)",
|
||||
"platform_folder_config_current": "Configurar pasta de download para {0}\nAtual: {1}",
|
||||
"platform_folder_config_default": "Configurar pasta de download para {0}\nUsando localização padrão",
|
||||
"platform_folder_show_current": "Mostrar caminho atual",
|
||||
"platform_folder_browse": "Navegar",
|
||||
"platform_folder_reset": "Redefinir para padrão",
|
||||
"platform_folder_set": "Pasta definida para {0}: {1}",
|
||||
"platform_folder_default_path": "Padrão: {0}",
|
||||
"folder_browser_title": "Selecionar pasta para {0}",
|
||||
"folder_browser_title_history_move": "Selecionar pasta de destino",
|
||||
"folder_browser_parent": "Pasta superior",
|
||||
"folder_browser_enter": "Entrar",
|
||||
"folder_browser_select": "Selecionar",
|
||||
"folder_new_folder": "Nova pasta",
|
||||
"folder_new_title": "Criar nova pasta",
|
||||
"folder_new_confirm": "Criar",
|
||||
"folder_created": "Pasta criada: {0}",
|
||||
"folder_create_error": "Erro ao criar: {0}",
|
||||
"controls_action_select_char": "Adicionar",
|
||||
"folder_browser_browse": "Explorar"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ import re
|
||||
import config # paths, settings, SAVE_FOLDER, etc.
|
||||
from utils import load_api_keys as _prime_api_keys # ensure API key files are created
|
||||
import network as network_mod # for progress_queues access
|
||||
from utils import load_sources, load_games, is_extension_supported, load_extensions_json, sanitize_filename, extract_zip_data
|
||||
from utils import load_sources, load_games, is_extension_supported, load_extensions_json, sanitize_filename
|
||||
from history import load_history, save_history, add_to_history
|
||||
from network import download_rom, download_from_1fichier, is_1fichier_url
|
||||
from rgsx_settings import get_sources_zip_url
|
||||
@@ -277,7 +277,7 @@ def cmd_games(args):
|
||||
suggestions = [] # (priority, score, game_obj)
|
||||
# 1) Substring match (full or sans extension) priority 0, score = position
|
||||
for g in games:
|
||||
title = g[0] if isinstance(g, (list, tuple)) and g else None
|
||||
title = g.name
|
||||
if not title:
|
||||
continue
|
||||
t_lower = title.lower()
|
||||
@@ -303,7 +303,7 @@ def cmd_games(args):
|
||||
# 2) Ordered non-contiguous tokens (priority 1)
|
||||
if q_tokens:
|
||||
for g in games:
|
||||
title = g[0] if isinstance(g, (list, tuple)) and g else None
|
||||
title = g.name
|
||||
if not title:
|
||||
continue
|
||||
tt = _tokens(title)
|
||||
@@ -313,7 +313,7 @@ def cmd_games(args):
|
||||
# 3) All tokens present, any order (priority 2), score = token set size
|
||||
if q_tokens:
|
||||
for g in games:
|
||||
title = g[0] if isinstance(g, (list, tuple)) and g else None
|
||||
title = g.name
|
||||
if not title:
|
||||
continue
|
||||
t_tokens = set(_tokens(title))
|
||||
@@ -322,12 +322,12 @@ def cmd_games(args):
|
||||
# Deduplicate by title keeping best (lowest priority, then score)
|
||||
best = {}
|
||||
for prio, score, g in suggestions:
|
||||
title = g[0] if isinstance(g, (list, tuple)) and g else str(g)
|
||||
title = g.name
|
||||
key = title.lower()
|
||||
cur = best.get(key)
|
||||
if cur is None or (prio, score) < (cur[0], cur[1]):
|
||||
best[key] = (prio, score, g)
|
||||
ranked = sorted(best.values(), key=lambda x: (x[0], x[1], (x[2][0] if isinstance(x[2], (list, tuple)) and x[2] else str(x[2])).lower()))
|
||||
ranked = sorted(best.values(), key=lambda x: (x[0], x[1], (x[2].name if isinstance(x[2], (list, config.Game)) and x[2] else str(x[2])).lower()))
|
||||
games = [g for _, _, g in ranked]
|
||||
# Table: Name (60) | Size (12) to allow "xxxx.xx MiB"
|
||||
NAME_W = 60
|
||||
@@ -344,7 +344,7 @@ def cmd_games(args):
|
||||
print(header)
|
||||
print(border)
|
||||
for g in games:
|
||||
title = g[0] if isinstance(g, (list, tuple)) and g else str(g)
|
||||
title = g.name
|
||||
size_val = ''
|
||||
if isinstance(g, (list, tuple)) and len(g) >= 3:
|
||||
size_val = display_size(g[2])
|
||||
@@ -447,11 +447,11 @@ def cmd_download(args):
|
||||
def _tokens(s: str) -> list[str]:
|
||||
return re.findall(r"[a-z0-9]+", s.lower())
|
||||
|
||||
def _game_title(g) -> str | None:
|
||||
return g[0] if isinstance(g, (list, tuple)) and g else None
|
||||
def _game_title(g: config.Game) -> str | None:
|
||||
return g.name
|
||||
|
||||
def _game_url(g) -> str | None:
|
||||
return g[1] if isinstance(g, (list, tuple)) and len(g) > 1 else None
|
||||
def _game_url(g: config.Game) -> str | None:
|
||||
return g.url
|
||||
|
||||
# 1) Exact match (case-insensitive), with and without extension
|
||||
match = None
|
||||
@@ -561,8 +561,8 @@ def cmd_download(args):
|
||||
size_val = ''
|
||||
size_raw = None
|
||||
for g in games:
|
||||
if isinstance(g, (list, tuple)) and g and g[0] == title and len(g) >= 3:
|
||||
size_raw = g[2]
|
||||
if g.name == title:
|
||||
size_raw = g.size
|
||||
break
|
||||
if size_raw is not None:
|
||||
size_val = display_size(size_raw)
|
||||
|
||||
@@ -49,7 +49,7 @@ def load_rgsx_settings():
|
||||
"""Charge tous les paramètres depuis rgsx_settings.json."""
|
||||
from config import RGSX_SETTINGS_PATH
|
||||
|
||||
logger.debug(f"Chargement des settings depuis: {RGSX_SETTINGS_PATH}")
|
||||
#logger.debug(f"Chargement des settings depuis: {RGSX_SETTINGS_PATH}")
|
||||
|
||||
default_settings = {
|
||||
"language": "en",
|
||||
@@ -75,15 +75,18 @@ def load_rgsx_settings():
|
||||
},
|
||||
"show_unsupported_platforms": False,
|
||||
"allow_unknown_extensions": False,
|
||||
"nintendo_layout": False,
|
||||
"roms_folder": "",
|
||||
"web_service_at_boot": False
|
||||
"web_service_at_boot": False,
|
||||
"last_gamelist_update": None,
|
||||
"platform_custom_paths": {} # Chemins personnalisés par plateforme
|
||||
}
|
||||
|
||||
try:
|
||||
if os.path.exists(RGSX_SETTINGS_PATH):
|
||||
with open(RGSX_SETTINGS_PATH, 'r', encoding='utf-8') as f:
|
||||
settings = json.load(f)
|
||||
logger.debug(f"Settings JSON chargé: display={settings.get('display', {})}")
|
||||
#logger.debug(f"Settings JSON chargé: display={settings.get('display', {})}")
|
||||
# Fusionner avec les valeurs par défaut pour assurer la compatibilité
|
||||
for key, value in default_settings.items():
|
||||
if key not in settings:
|
||||
@@ -110,6 +113,27 @@ def save_rgsx_settings(settings):
|
||||
print(f"Erreur lors de la sauvegarde de rgsx_settings.json: {str(e)}")
|
||||
|
||||
|
||||
def get_last_gamelist_update(settings=None):
|
||||
"""Récupère la date de dernière mise à jour de la liste des jeux."""
|
||||
if settings is None:
|
||||
settings = load_rgsx_settings()
|
||||
return settings.get("last_gamelist_update", None)
|
||||
|
||||
|
||||
def set_last_gamelist_update(date_string=None):
|
||||
"""Définit la date de dernière mise à jour de la liste des jeux.
|
||||
Si date_string est None, utilise la date actuelle.
|
||||
"""
|
||||
from datetime import datetime
|
||||
settings = load_rgsx_settings()
|
||||
if date_string is None:
|
||||
date_string = datetime.now().strftime("%Y-%m-%d")
|
||||
settings["last_gamelist_update"] = date_string
|
||||
save_rgsx_settings(settings)
|
||||
logger.info(f"Date de dernière mise à jour de la liste des jeux: {date_string}")
|
||||
return date_string
|
||||
|
||||
|
||||
|
||||
def load_symlink_settings():
|
||||
"""Load symlink settings from rgsx_settings.json."""
|
||||
@@ -275,6 +299,22 @@ def set_allow_unknown_extensions(enabled: bool) -> bool:
|
||||
save_rgsx_settings(settings)
|
||||
return settings["allow_unknown_extensions"]
|
||||
|
||||
|
||||
# ----------------------- Invert ABXY layout ----------------------- #
|
||||
def get_nintendo_layout(settings=None) -> bool:
|
||||
"""Retourne True si l'inversion ABXY (icônes) est activée."""
|
||||
if settings is None:
|
||||
settings = load_rgsx_settings()
|
||||
return bool(settings.get("nintendo_layout", False))
|
||||
|
||||
|
||||
def set_nintendo_layout(enabled: bool) -> bool:
|
||||
"""Active/désactive l'inversion ABXY (icônes) et sauvegarde."""
|
||||
settings = load_rgsx_settings()
|
||||
settings["nintendo_layout"] = bool(enabled)
|
||||
save_rgsx_settings(settings)
|
||||
return settings["nintendo_layout"]
|
||||
|
||||
# ----------------------- Hide premium systems toggle ----------------------- #
|
||||
|
||||
def get_hide_premium_systems(settings=None) -> bool:
|
||||
@@ -457,3 +497,66 @@ def save_game_filters(filters_dict):
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving game filters: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
def get_platform_custom_path(platform_name):
|
||||
"""Récupère le chemin personnalisé pour une plateforme."""
|
||||
try:
|
||||
settings = load_rgsx_settings()
|
||||
custom_paths = settings.get("platform_custom_paths", {})
|
||||
return custom_paths.get(platform_name, "")
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting platform custom path: {str(e)}")
|
||||
return ""
|
||||
|
||||
|
||||
def set_platform_custom_path(platform_name, path):
|
||||
"""Définit le chemin personnalisé pour une plateforme."""
|
||||
try:
|
||||
settings = load_rgsx_settings()
|
||||
if "platform_custom_paths" not in settings:
|
||||
settings["platform_custom_paths"] = {}
|
||||
if path:
|
||||
settings["platform_custom_paths"][platform_name] = path
|
||||
else:
|
||||
# Si le chemin est vide, supprimer l'entrée
|
||||
settings["platform_custom_paths"].pop(platform_name, None)
|
||||
save_rgsx_settings(settings)
|
||||
logger.info(f"Platform custom path set: {platform_name} -> {path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting platform custom path: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
def get_all_platform_custom_paths():
|
||||
"""Récupère tous les chemins personnalisés des plateformes."""
|
||||
try:
|
||||
settings = load_rgsx_settings()
|
||||
return settings.get("platform_custom_paths", {})
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting all platform custom paths: {str(e)}")
|
||||
return {}
|
||||
|
||||
|
||||
def get_auto_extract():
|
||||
"""Récupère le paramètre d'extraction automatique des archives après téléchargement."""
|
||||
try:
|
||||
settings = load_rgsx_settings()
|
||||
return settings.get("auto_extract", True) # Activé par défaut
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting auto_extract setting: {str(e)}")
|
||||
return True
|
||||
|
||||
|
||||
def set_auto_extract(enabled: bool):
|
||||
"""Définit le paramètre d'extraction automatique des archives après téléchargement."""
|
||||
try:
|
||||
settings = load_rgsx_settings()
|
||||
settings["auto_extract"] = enabled
|
||||
save_rgsx_settings(settings)
|
||||
logger.info(f"Auto extract set to: {enabled}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting auto_extract: {str(e)}")
|
||||
return False
|
||||
|
||||
@@ -21,10 +21,11 @@ from datetime import datetime, timezone
|
||||
from email.utils import formatdate, parsedate_to_datetime
|
||||
import config
|
||||
from history import load_history, save_history
|
||||
from utils import load_sources, load_games, extract_data
|
||||
from utils import load_sources, load_games, extract_data, get_clean_display_name, parse_torrent_download_url
|
||||
from network import download_rom, download_from_1fichier
|
||||
from pathlib import Path
|
||||
from rgsx_settings import get_language
|
||||
from config import Game
|
||||
|
||||
try:
|
||||
from watchdog.observers import Observer # type: ignore
|
||||
@@ -161,7 +162,7 @@ def get_cached_sources() -> tuple[list[dict], str, datetime]:
|
||||
return copy.deepcopy(platforms), etag, last_modified
|
||||
|
||||
|
||||
def get_cached_games(platform: str) -> tuple[list[tuple], str, datetime]:
|
||||
def get_cached_games(platform: str) -> tuple[list[Game], str, datetime]:
|
||||
"""Return cached games list for platform with metadata."""
|
||||
now = time.time()
|
||||
with cache_lock:
|
||||
@@ -696,14 +697,14 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
elif games_last_modified:
|
||||
latest_modified = games_last_modified
|
||||
for game in games:
|
||||
game_name = game[0] if isinstance(game, (list, tuple)) else str(game)
|
||||
game_name = game.name
|
||||
game_name_lower = game_name.lower()
|
||||
if all(word in game_name_lower for word in search_words):
|
||||
matching_games.append({
|
||||
'game_name': game_name,
|
||||
'platform': platform_name,
|
||||
'url': game[1] if len(game) > 1 and isinstance(game, (list, tuple)) else None,
|
||||
'size': normalize_size(game[2] if len(game) > 2 and isinstance(game, (list, tuple)) else None, self._get_language_from_cookies())
|
||||
'url': game.url,
|
||||
'size': normalize_size(game.size, self._get_language_from_cookies())
|
||||
})
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur lors de la recherche dans {platform_name}: {e}")
|
||||
@@ -750,9 +751,9 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
games, _, games_last_modified = get_cached_games(platform_name)
|
||||
games_formatted = [
|
||||
{
|
||||
'name': g[0],
|
||||
'url': g[1] if len(g) > 1 else None,
|
||||
'size': normalize_size(g[2] if len(g) > 2 else None, lang)
|
||||
'name': g.name,
|
||||
'url': g.url,
|
||||
'size': normalize_size(g.size, lang)
|
||||
}
|
||||
for g in games
|
||||
]
|
||||
@@ -865,9 +866,27 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
# Route: API - Settings (lecture)
|
||||
elif path == '/api/settings':
|
||||
try:
|
||||
from rgsx_settings import load_rgsx_settings
|
||||
from rgsx_settings import load_rgsx_settings, get_auto_extract
|
||||
from utils import check_web_service_status, check_custom_dns_status, load_api_keys
|
||||
settings = load_rgsx_settings()
|
||||
|
||||
# Ajouter les options dynamiques
|
||||
settings['auto_extract'] = get_auto_extract()
|
||||
|
||||
# Options Linux/Batocera
|
||||
if config.OPERATING_SYSTEM == "Linux":
|
||||
settings['web_service_at_boot'] = check_web_service_status()
|
||||
settings['custom_dns_at_boot'] = check_custom_dns_status()
|
||||
|
||||
# API Keys (filtrer la clé 'reloaded' qui n'est pas utile pour l'UI)
|
||||
api_keys_data = load_api_keys()
|
||||
settings['api_keys'] = {
|
||||
'1fichier': api_keys_data.get('1fichier', ''),
|
||||
'alldebrid': api_keys_data.get('alldebrid', ''),
|
||||
'debridlink': api_keys_data.get('debridlink', ''),
|
||||
'realdebrid': api_keys_data.get('realdebrid', '')
|
||||
}
|
||||
|
||||
self._send_json({
|
||||
'success': True,
|
||||
'settings': settings,
|
||||
@@ -1117,7 +1136,7 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
if game_name_param and game_index is None:
|
||||
game_index = None
|
||||
for idx, game in enumerate(games):
|
||||
current_game_name = game[0] if isinstance(game, (list, tuple)) else str(game)
|
||||
current_game_name = game.name
|
||||
if current_game_name == game_name_param:
|
||||
game_index = idx
|
||||
break
|
||||
@@ -1138,13 +1157,22 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
return
|
||||
|
||||
game = games[game_index]
|
||||
game_name = game[0]
|
||||
game_url = game[1] if len(game) > 1 else None
|
||||
game_name = game.name
|
||||
game_url = game.url
|
||||
|
||||
if not game_url:
|
||||
torrent_message = TRANSLATIONS.get('popup_torrent_in_maintenance', 'torrent in maintence')
|
||||
self._send_json({
|
||||
'success': False,
|
||||
'error': 'URL de téléchargement non disponible'
|
||||
'error': torrent_message
|
||||
}, status=400)
|
||||
return
|
||||
|
||||
if parse_torrent_download_url(game_url) is not None:
|
||||
torrent_message = TRANSLATIONS.get('popup_torrent_in_maintenance', 'torrent in maintence')
|
||||
self._send_json({
|
||||
'success': False,
|
||||
'error': torrent_message
|
||||
}, status=400)
|
||||
return
|
||||
|
||||
@@ -1224,6 +1252,7 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
queue_history_entry = {
|
||||
'platform': platform,
|
||||
'game_name': game_name,
|
||||
'display_name': get_clean_display_name(game_name, platform),
|
||||
'status': 'Queued',
|
||||
'url': game_url,
|
||||
'progress': 0,
|
||||
@@ -1261,6 +1290,7 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
download_history_entry = {
|
||||
'platform': platform,
|
||||
'game_name': game_name,
|
||||
'display_name': get_clean_display_name(game_name, platform),
|
||||
'status': 'Downloading',
|
||||
'url': game_url,
|
||||
'progress': 0,
|
||||
@@ -1470,7 +1500,8 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
# Route: Sauvegarder les settings
|
||||
elif path == '/api/settings':
|
||||
try:
|
||||
from rgsx_settings import save_rgsx_settings
|
||||
from rgsx_settings import save_rgsx_settings, set_auto_extract
|
||||
from utils import toggle_web_service_at_boot, toggle_custom_dns_at_boot, save_api_keys
|
||||
|
||||
settings = data.get('settings')
|
||||
if not settings:
|
||||
@@ -1480,6 +1511,37 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
}, status=400)
|
||||
return
|
||||
|
||||
# Gérer auto_extract séparément
|
||||
if 'auto_extract' in settings:
|
||||
set_auto_extract(settings['auto_extract'])
|
||||
del settings['auto_extract'] # Ne pas sauvegarder dans le fichier principal
|
||||
|
||||
# Gérer web_service_at_boot (Linux only)
|
||||
if 'web_service_at_boot' in settings:
|
||||
if config.OPERATING_SYSTEM == "Linux":
|
||||
try:
|
||||
toggle_web_service_at_boot(settings['web_service_at_boot'])
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur toggle web service: {e}")
|
||||
del settings['web_service_at_boot']
|
||||
|
||||
# Gérer custom_dns_at_boot (Linux only)
|
||||
if 'custom_dns_at_boot' in settings:
|
||||
if config.OPERATING_SYSTEM == "Linux":
|
||||
try:
|
||||
toggle_custom_dns_at_boot(settings['custom_dns_at_boot'])
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur toggle custom DNS: {e}")
|
||||
del settings['custom_dns_at_boot']
|
||||
|
||||
# Gérer API keys séparément
|
||||
if 'api_keys' in settings:
|
||||
try:
|
||||
save_api_keys(settings['api_keys'])
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur sauvegarde API keys: {e}")
|
||||
del settings['api_keys']
|
||||
|
||||
save_rgsx_settings(settings)
|
||||
|
||||
self._send_json({
|
||||
@@ -2019,18 +2081,47 @@ def run_server(host='0.0.0.0', port=5000):
|
||||
class ReuseAddrHTTPServer(HTTPServer):
|
||||
allow_reuse_address = True
|
||||
|
||||
# Tuer les processus existants utilisant le port
|
||||
# Tuer les processus existants utilisant le port (plateforme spécifique)
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run(['lsof', '-ti', f':{port}'], capture_output=True, text=True, timeout=2)
|
||||
pids = result.stdout.strip().split('\n')
|
||||
for pid in pids:
|
||||
if pid:
|
||||
try:
|
||||
subprocess.run(['kill', '-9', pid], timeout=2)
|
||||
logger.info(f"Processus {pid} tué (port {port} libéré)")
|
||||
except Exception as e:
|
||||
logger.warning(f"Impossible de tuer le processus {pid}: {e}")
|
||||
# Windows: utiliser netstat + taskkill
|
||||
if os.name == 'nt' or getattr(config, 'OPERATING_SYSTEM', '').lower() == 'windows':
|
||||
try:
|
||||
netstat = subprocess.run(['netstat', '-ano'], capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=3)
|
||||
lines = netstat.stdout.splitlines()
|
||||
pids = set()
|
||||
for line in lines:
|
||||
parts = line.split()
|
||||
if len(parts) >= 5:
|
||||
local = parts[1]
|
||||
pid = parts[-1]
|
||||
if local.endswith(f':{port}'):
|
||||
pids.add(pid)
|
||||
for pid in pids:
|
||||
# Safer: ignore PID 0 and non-numeric entries (system / header lines)
|
||||
if not pid or not pid.isdigit():
|
||||
continue
|
||||
pid_int = int(pid)
|
||||
if pid_int <= 0:
|
||||
continue
|
||||
try:
|
||||
subprocess.run(['taskkill', '/PID', pid, '/F'], timeout=3)
|
||||
logger.info(f"Processus {pid} tué (port {port} libéré) [Windows]")
|
||||
except Exception as e:
|
||||
logger.warning(f"Impossible de tuer le processus {pid}: {e}")
|
||||
except Exception as e:
|
||||
logger.debug(f"Windows port release check failed: {e}")
|
||||
else:
|
||||
# Unix-like: utiliser lsof + kill
|
||||
result = subprocess.run(['lsof', '-ti', f':{port}'], capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=2)
|
||||
pids = result.stdout.strip().split('\n')
|
||||
for pid in pids:
|
||||
if pid:
|
||||
try:
|
||||
subprocess.run(['kill', '-9', pid], timeout=2)
|
||||
logger.info(f"Processus {pid} tué (port {port} libéré)")
|
||||
except Exception as e:
|
||||
logger.warning(f"Impossible de tuer le processus {pid}: {e}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Impossible de libérer le port {port}: {e}")
|
||||
|
||||
|
||||
@@ -540,3 +540,24 @@ header p { opacity: 0.9; font-size: 1.1em; }
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
/* System Info Collapse/Details */
|
||||
details summary {
|
||||
list-style: none;
|
||||
}
|
||||
details summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
details summary .collapse-arrow {
|
||||
transition: transform 0.3s ease;
|
||||
display: inline-block;
|
||||
}
|
||||
details[open] summary .collapse-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
details[open] summary {
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
details summary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@@ -1866,101 +1866,107 @@
|
||||
const showUnsupportedLabel = t('web_settings_show_unsupported');
|
||||
const allowUnknownLabel = t('web_settings_allow_unknown');
|
||||
|
||||
// Construire la section d'informations système détaillées
|
||||
// Construire la section d'informations système détaillées (dans un collapse fermé par défaut)
|
||||
let systemInfoHTML = '';
|
||||
if (systemInfo && (systemInfo.model || systemInfo.cpu_model)) {
|
||||
systemInfoHTML = `
|
||||
<h3 style="margin-top: 20px; margin-bottom: 15px;">🖥️ System Information</h3>
|
||||
<div class="info-grid" style="margin-bottom: 20px; background: #f0f8ff; padding: 15px; border-radius: 8px; border: 2px solid #007bff;">
|
||||
${systemInfo.model ? `
|
||||
<details style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<summary style="cursor: pointer; padding: 12px 15px; background: linear-gradient(135deg, #007bff 0%, #0056b3 100%); color: white; border-radius: 8px; font-weight: bold; font-size: 1.1em; list-style: none; display: flex; align-items: center; gap: 10px;">
|
||||
<span class="collapse-arrow">▶</span>
|
||||
🖥️ ${t('web_system_info_title') || 'System Information'}
|
||||
<span style="margin-left: auto; font-size: 0.85em; opacity: 0.9;">${systemInfo.model || systemInfo.system || ''}</span>
|
||||
</summary>
|
||||
<div class="info-grid" style="margin-top: 10px; background: #f0f8ff; padding: 15px; border-radius: 0 0 8px 8px; border: 2px solid #007bff; border-top: none;">
|
||||
${systemInfo.model ? `
|
||||
<div class="info-item">
|
||||
<strong>💻 Model</strong>
|
||||
${systemInfo.model}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.system ? `
|
||||
<div class="info-item">
|
||||
<strong>🐧 System</strong>
|
||||
${systemInfo.system}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.architecture ? `
|
||||
<div class="info-item">
|
||||
<strong>⚙️ Architecture</strong>
|
||||
${systemInfo.architecture}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_model ? `
|
||||
<div class="info-item">
|
||||
<strong>🔧 CPU Model</strong>
|
||||
${systemInfo.cpu_model}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_cores ? `
|
||||
<div class="info-item">
|
||||
<strong>🧮 CPU Cores</strong>
|
||||
${systemInfo.cpu_cores}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_max_frequency ? `
|
||||
<div class="info-item">
|
||||
<strong>⚡ CPU Frequency</strong>
|
||||
${systemInfo.cpu_max_frequency}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_features ? `
|
||||
<div class="info-item">
|
||||
<strong>✨ CPU Features</strong>
|
||||
${systemInfo.cpu_features}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.temperature ? `
|
||||
<div class="info-item">
|
||||
<strong>🌡️ Temperature</strong>
|
||||
${systemInfo.temperature}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.available_memory && systemInfo.total_memory ? `
|
||||
<div class="info-item">
|
||||
<strong>💾 Memory</strong>
|
||||
${systemInfo.available_memory} / ${systemInfo.total_memory}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.display_resolution ? `
|
||||
<div class="info-item">
|
||||
<strong>🖥️ Display Resolution</strong>
|
||||
${systemInfo.display_resolution}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.display_refresh_rate ? `
|
||||
<div class="info-item">
|
||||
<strong>🔄 Refresh Rate</strong>
|
||||
${systemInfo.display_refresh_rate}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.data_partition_format ? `
|
||||
<div class="info-item">
|
||||
<strong>💽 Partition Format</strong>
|
||||
${systemInfo.data_partition_format}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.data_partition_space ? `
|
||||
<div class="info-item">
|
||||
<strong>💿 Available Space</strong>
|
||||
${systemInfo.data_partition_space}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.network_ip ? `
|
||||
<div class="info-item">
|
||||
<strong>🌐 Network IP</strong>
|
||||
${systemInfo.network_ip}
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="info-item">
|
||||
<strong>💻 Model</strong>
|
||||
${systemInfo.model}
|
||||
<strong>🎮 ${platformsCountLabel}</strong>
|
||||
${info.platforms_count}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.system ? `
|
||||
<div class="info-item">
|
||||
<strong>🐧 System</strong>
|
||||
${systemInfo.system}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.architecture ? `
|
||||
<div class="info-item">
|
||||
<strong>⚙️ Architecture</strong>
|
||||
${systemInfo.architecture}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_model ? `
|
||||
<div class="info-item">
|
||||
<strong>🔧 CPU Model</strong>
|
||||
${systemInfo.cpu_model}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_cores ? `
|
||||
<div class="info-item">
|
||||
<strong>🧮 CPU Cores</strong>
|
||||
${systemInfo.cpu_cores}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_max_frequency ? `
|
||||
<div class="info-item">
|
||||
<strong>⚡ CPU Frequency</strong>
|
||||
${systemInfo.cpu_max_frequency}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.cpu_features ? `
|
||||
<div class="info-item">
|
||||
<strong>✨ CPU Features</strong>
|
||||
${systemInfo.cpu_features}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.temperature ? `
|
||||
<div class="info-item">
|
||||
<strong>🌡️ Temperature</strong>
|
||||
${systemInfo.temperature}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.available_memory && systemInfo.total_memory ? `
|
||||
<div class="info-item">
|
||||
<strong>💾 Memory</strong>
|
||||
${systemInfo.available_memory} / ${systemInfo.total_memory}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.display_resolution ? `
|
||||
<div class="info-item">
|
||||
<strong>🖥️ Display Resolution</strong>
|
||||
${systemInfo.display_resolution}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.display_refresh_rate ? `
|
||||
<div class="info-item">
|
||||
<strong>🔄 Refresh Rate</strong>
|
||||
${systemInfo.display_refresh_rate}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.data_partition_format ? `
|
||||
<div class="info-item">
|
||||
<strong>💽 Partition Format</strong>
|
||||
${systemInfo.data_partition_format}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.data_partition_space ? `
|
||||
<div class="info-item">
|
||||
<strong>💿 Available Space</strong>
|
||||
${systemInfo.data_partition_space}
|
||||
</div>
|
||||
` : ''}
|
||||
${systemInfo.network_ip ? `
|
||||
<div class="info-item">
|
||||
<strong>🌐 Network IP</strong>
|
||||
${systemInfo.network_ip}
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="info-item">
|
||||
<strong>🎮 ${platformsCountLabel}</strong>
|
||||
${info.platforms_count}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -2057,6 +2063,13 @@
|
||||
placeholder="${t('web_settings_custom_url_placeholder')}">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="setting-auto-extract" ${settings.auto_extract !== false ? 'checked' : ''}>
|
||||
<span>📦 ${t('web_settings_auto_extract')}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="setting-show-unsupported" ${settings.show_unsupported_platforms ? 'checked' : ''}>
|
||||
@@ -2071,6 +2084,50 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
${info.system === 'Linux' ? `
|
||||
<h4 style="margin-top: 25px; margin-bottom: 15px; border-top: 1px solid #ddd; padding-top: 15px;">🐧 Linux/Batocera Options</h4>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="setting-web-service" ${settings.web_service_at_boot ? 'checked' : ''}>
|
||||
<span>🌐 ${t('web_settings_web_service')}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="setting-custom-dns" ${settings.custom_dns_at_boot ? 'checked' : ''}>
|
||||
<span>🔒 ${t('web_settings_custom_dns')}</span>
|
||||
</label>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<h4 style="margin-top: 25px; margin-bottom: 15px; border-top: 1px solid #ddd; padding-top: 15px;">🔑 API Keys</h4>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label>1fichier API Key</label>
|
||||
<input type="password" id="setting-api-1fichier" value="${settings.api_keys?.['1fichier'] || ''}"
|
||||
placeholder="Enter 1fichier API key">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label>AllDebrid API Key</label>
|
||||
<input type="password" id="setting-api-alldebrid" value="${settings.api_keys?.alldebrid || ''}"
|
||||
placeholder="Enter AllDebrid API key">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label>Debrid-Link API Key</label>
|
||||
<input type="password" id="setting-api-debridlink" value="${settings.api_keys?.debridlink || ''}"
|
||||
placeholder="Enter Debrid-Link API key">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label>RealDebrid API Key</label>
|
||||
<input type="password" id="setting-api-realdebrid" value="${settings.api_keys?.realdebrid || ''}"
|
||||
placeholder="Enter RealDebrid API key">
|
||||
</div>
|
||||
|
||||
<button id="save-settings-btn" style="width: 100%; background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; border: none; padding: 15px; border-radius: 8px; font-size: 18px; font-weight: bold; cursor: pointer; margin-top: 10px;">
|
||||
💾 ${t('web_settings_save')}
|
||||
</button>
|
||||
@@ -2127,7 +2184,18 @@
|
||||
},
|
||||
show_unsupported_platforms: document.getElementById('setting-show-unsupported').checked,
|
||||
allow_unknown_extensions: document.getElementById('setting-allow-unknown').checked,
|
||||
auto_extract: document.getElementById('setting-auto-extract').checked,
|
||||
roms_folder: document.getElementById('setting-roms-folder').value.trim(),
|
||||
// Linux/Batocera options (only if elements exist)
|
||||
web_service_at_boot: document.getElementById('setting-web-service')?.checked || false,
|
||||
custom_dns_at_boot: document.getElementById('setting-custom-dns')?.checked || false,
|
||||
// API Keys
|
||||
api_keys: {
|
||||
'1fichier': document.getElementById('setting-api-1fichier')?.value.trim() || '',
|
||||
'alldebrid': document.getElementById('setting-api-alldebrid')?.value.trim() || '',
|
||||
'debridlink': document.getElementById('setting-api-debridlink')?.value.trim() || '',
|
||||
'realdebrid': document.getElementById('setting-api-realdebrid')?.value.trim() || ''
|
||||
},
|
||||
game_filters: {
|
||||
region_filters: regionFiltersObj,
|
||||
hide_non_release: document.getElementById('hide-non-release')?.checked || savedHideNonRelease,
|
||||
|
||||
1066
ports/RGSX/utils.py
1066
ports/RGSX/utils.py
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "2.4.0.1"
|
||||
"version": "2.6.1.6.1"
|
||||
}
|
||||
@@ -301,6 +301,7 @@ set PYGAME_HIDE_SUPPORT_PROMPT=1
|
||||
set SDL_VIDEODRIVER=windows
|
||||
set SDL_AUDIODRIVER=directsound
|
||||
set PYTHONWARNINGS=ignore::UserWarning:pygame.pkgdata
|
||||
set PYTHONIOENCODING=utf-8
|
||||
|
||||
:: =============================================================================
|
||||
:: Configuration multi-ecran
|
||||
@@ -331,6 +332,7 @@ echo [%DATE% %TIME%] Environment variables set: >> "%LOG_FILE%"
|
||||
echo [%DATE% %TIME%] RGSX_ROOT=%RGSX_ROOT% >> "%LOG_FILE%"
|
||||
echo [%DATE% %TIME%] SDL_VIDEODRIVER=%SDL_VIDEODRIVER% >> "%LOG_FILE%"
|
||||
echo [%DATE% %TIME%] SDL_AUDIODRIVER=%SDL_AUDIODRIVER% >> "%LOG_FILE%"
|
||||
echo [%DATE% %TIME%] PYTHONIOENCODING=%PYTHONIOENCODING% >> "%LOG_FILE%"
|
||||
|
||||
echo.
|
||||
if defined DISPLAY_NUM (
|
||||
|
||||
Reference in New Issue
Block a user