Compare commits

...

38 Commits

Author SHA1 Message Date
skymike03
6813a0bc3d v2.6.1.5 (2026.04.01)
- test implant torrent handling for minerva support (disabled for now)
2026-04-01 18:34:48 +02:00
skymike03
21b39c66b9 v2.6.1.4 (2026.03.30)
- Add browser-like headers for file downloads with debrids and enhance AllDebrid link handling
2026-03-30 21:12:59 +02:00
skymike03
42b2204aeb Reverted back to original version after test 2026-03-22 12:25:45 +01:00
skymike03
67a38c45aa ## v2.6.3.1.0 TEST (2026.03.22)
- Test discord auto release changelog
- Test
2026-03-22 12:20:46 +01:00
skymike03
893b73ecc5 Refactor Discord changelog notification step in release workflow 2026-03-22 12:09:32 +01:00
skymike03
5e1a684275 Enhance Discord notifications with changelog and bot details 2026-03-22 12:06:06 +01:00
skymike03
9226a818f3 v2.6.3.1 (test update) 2026-03-22 11:56:58 +01:00
skymike03
2fd1bcaf01 test discord 2026-03-22 11:55:58 +01:00
skymike03
875bf8fa23 v2.6.1.3 (2026.03.21)
- add update changelog on start before applying new update
2026-03-21 18:26:39 +01:00
skymike03
f9cbf0196e v2.6.1.2 (2026.03.21)
- added paging navigation on folder browser and full page list
2026-03-21 17:36:06 +01:00
skymike03
eb86d69895 v2.6.1.1 (2026.21.03)
- Improved History/Downloads table readability by giving more space to game titles and using middle truncation for long names
- Cleaned displayed game names to remove platform/path prefixes from titles
- Improved file matching for downloaded and extracted games, including support for filename variants and tag differences
- Updated Locate file to show all matching files instead of only one path
- Added a Move action from the locate screen, using the existing folder browser to move all listed files to a selected destination
- Added collision-safe file moves and persisted moved paths in history
- Added localized labels/messages for the new move flow
- Fixed a startup crash caused by a translation function name conflict
- Fixed navigation after move so OK and Back work correctly from the locate screen
2026-03-21 17:29:39 +01:00
skymike03
b09b3da371 v2.6.1.0 (2026.03.20)
- Added the IP address to the top-right info badge.
- Added disk usage to the left badge using a used/total(percentage) format.
- Added screen resolution below disk usage on the platforms page.
- Improved entry speed for global cross-platform search.
- Fixed virtual keyboard positioning in the search screen.
- Adjusted the platform grid to avoid overlapping the footer on small resolutions.
- Made the header badges responsive to prevent overlap on smaller screens.
- Made the footer responsive with automatic scaling of text, icons, and spacing.
- Asking to update gamelist once a day
2026-03-20 19:14:27 +01:00
skymike03
0915a90fbe Merge branch 'main' of https://github.com/RetroGameSets/RGSX 2026-03-17 23:24:33 +01:00
skymike03
3ae3c151eb v2.6.0.3 (2025.03.17)
- Add support and donation information to release notes
- Add normalize game name for roms scanning (ie. Game (USA).ext will be shown owned for a rom named only "Game.ext"
- Add fulscreen/windowed mode in Settings > Display with auto resize window
2026-03-17 23:24:31 +01:00
RGS
7460b12d71 Delete snes directory error 2026-03-17 23:16:49 +01:00
skymike03
2f437c1aa4 v2.6.0.2 (2025.03.17)
- Add support and donation information to release notes
- Add normalize game name for roms scanning (ie. Game (USA).ext will be shown owned for a rom named only "Game.ext"
2026-03-17 23:12:50 +01:00
skymike03
054b174c18 v2.6.0.1 (2025.03.16)
• add Debrid-Link API key support in desktop and web settings
• add Debrid-Link fallback for premium link generation in addition to AllDebrid and RealDebrid
2026-03-16 20:28:19 +01:00
skymike03
fbb1a2aa68 v2.5.0.7 (2026.03.16)
• improve filters/search performance with lazy cache without slowing unfiltered game list access
• fix fbneo logging traceback and avoid re-downloading/parsing fbneo gamelist when cache is already available
• add rom scan to rebuild downloaded games database from local files with extension-insensitive matching and backward compatibility
• fix clear history to remove stale converting entries and keep only real active transfers
• fix keyboard mode controls display to ignore joystick-only mappings and restore proper keyboard labels
• update displayed keyboard labels with ascii-safe names (Esc/Echap, Debut, Verr Def)
• move version/controller info to a top-right header badge and add page number badge on top-left
• simplify platform footer and loading footer layout
• add global search from platform menu across all available systems
• fix global search input handling and routing
• update global search confirm action to download directly and allow queue action from results
• add size and ext columns to global search results
• add ext column to game list and history
• add folder column to history to show download destination folder
• fix return from history to restore current game list instead of jumping back to platform list
• fix history crash caused by ext column text truncation
2026-03-16 19:09:47 +01:00
skymike03
1dbc741617 v2.5.0.6 (2026.03.15)
update download status handling for converting state and ps3 dec function
2026-03-15 23:47:32 +01:00
skymike03
c4913a5fc2 v2.5.0.5 (2026.03.05)
Merge pull request [#48](https://github.com/RetroGameSets/RGSX/issues/48) from elieserdejesus

- Fixing and upgrade Filters and search
- showing fbneo 'full name' instead rom name.
2026-03-05 18:45:01 +01:00
RGS
bf9d3d2de5 Merge pull request #48 from elieserdejesus/filters
Fixing Filters
2026-03-05 15:37:33 +01:00
Elieser de Jesus
9979949bdc Applying filters using 'display_name'
Adding a Game class with a display_name used do show games. The 'display_name'
is the game file name without suffix (.zip, .7z, .bin, etc) and without platform
prefix. Many platforms line NES, mega drive was showing the plaftorm name as prefix.

Now the filters are working with the 'display_name'.

I see the filters are no applyed until the "apply" button is clicked. Now the filters
are applyed everytime the game list is showed.
2026-03-05 11:06:07 -03:00
Elieser de Jesus
9ed264544f adding bootleg filter 2026-03-05 08:40:33 -03:00
Elieser de Jesus
779c060927 removing duplicated entries 2026-03-05 08:36:51 -03:00
RGS
88400e538f Merge pull request #47 from elieserdejesus/main
Showing and filtering FBneo games using 'full name' instead 'rom name'
2026-03-05 09:19:12 +01:00
Elieser de Jesus
cbab067dd6 filtering fbneo games by full name instead rom name 2026-03-04 18:45:18 -03:00
Elieser de Jesus
b4ed0b355d showing fbneo 'full name' instead rom name.
The full names are downloaded from github fbneo
repo only when user select fbneo in platforms screen
2026-03-04 18:42:21 -03:00
skymike03
51ad08ff33 v2.5.0.4 (2026.02.15)
- add some new cool musics :P
2026-02-15 20:34:28 +01:00
skymike03
d6a5c4b27e Add type ignore comment for requests import in network.py 2026-02-08 18:13:03 +01:00
skymike03
2c7c3414a5 v2.5.0.3 (2026.02.08)
- add 7z support for extract games
- add cookie test for archive.org downloads (new romhacks platforms added)
2026-02-08 16:58:11 +01:00
skymike03
059d3988ac v2.5.0.2 (2026.02.06)
- add "versionclean" script to clean batocera version after activating custom rgsx service at boot . Thanks to the BUA project for the script.
- add encoding UTF-8 for retrobat launcher to avoid controller with non ASCII characters to crash rgsx (issue #43) thanks to Crover81
- add new menu in Menu>Settings to test connection to all usefull urls and logging
- some log trim
2026-02-06 16:26:40 +01:00
skymike03
50c9b9caad v2.5.0.1 (2026.01.27)
- add Nintendo/Xbox Layout in Menu>Controls>Controls Help to invert displayed buttons
2026-01-29 18:58:56 +01:00
skymike03
7d2d55fe5f v2.5.0.0 (2026.01.17)
- add "disable auto-extract" function in MENU>SETTINGS
- add  "ROMS folder" option to select a custom folder for all downloads in MENU>SETTINGS (or Web Interface settings)
- add new menu to choose custom download folder for a specific system (long press CONFIRM button on a selected system/platform)
- add pause option in history when downloading games
- update submenus display layout to have more space for new options
- add missing settings options in rsgx_web  (disable auto-extract, API keys, activate web service / custom dns at boot)
2026-01-17 00:54:10 +01:00
skymike03
14a5416d2d v2.5.0.0 (2026.01.17)
- add "disable auto-extract" function in MENU>SETTINGS
- add  "ROMS folder" option to select a custom folder for all downloads in MENU>SETTINGS (or Web Interface settings)
- add new menu to choose custom download folder for a specific system (long press CONFIRM button on a selected system/platform)
- add pause option in history when downloading games
- update submenus display layout to have more space for new options
- add missing settings options in rsgx_web  (disable auto-extract, API keys, activate web service / custom dns at boot)
2026-01-17 00:45:41 +01:00
skymike03
3193dc90f6 v2.4.2.0 (2026.01.15)
- add menu to choose custom download folder for a specific system (long press validate on a system/platform)
- add pause menu when downloading game
2026-01-14 23:19:58 +01:00
skymike03
b437f31854 v2.4.1.0 (2026.01.14)
- add gamelist update check at rgsx start to warn if you didn't update gamlist since few days
- add view for grid mode (Settings > Display)
- use submenu for fonts (Settings > Display)
- performance mode (Settings>Display) updated, now it runs faster without any effects
2026-01-14 21:32:14 +01:00
skymike03
08f3e64d2a v2.4.0.2 (2026.01.14)
- correct some bugs/errors on display and logging functionality; update language files with new options
2026-01-14 20:25:47 +01:00
RGS
4968af2da9 Update image source in README.md 2026-01-07 14:48:30 +01:00
53 changed files with 7427 additions and 1572 deletions

View File

@@ -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
View File

@@ -23,3 +23,4 @@ pygame/
data/
docker-compose.test.yml
config/
pyrightconfig.json

View File

@@ -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">

View File

@@ -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":

View 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

View File

@@ -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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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

View File

@@ -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.5"
# 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

View File

@@ -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

View File

@@ -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

View File

@@ -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, {})

View File

@@ -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]

View File

@@ -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",
@@ -52,6 +57,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 +67,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 +116,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 +150,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 +197,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 +240,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 +267,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 +303,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 +318,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 +370,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 +396,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 +475,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"
}

View File

@@ -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",
@@ -52,6 +57,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 +67,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 +117,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 +171,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 +196,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 +223,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 +242,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 +302,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 +320,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 +372,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 +398,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 +475,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"
}

View File

@@ -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",
@@ -51,8 +56,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 +65,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 +114,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 +148,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 +197,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 +240,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 +267,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 +303,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 +318,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 +370,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 +396,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 +473,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"
}

View File

@@ -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",
@@ -52,6 +57,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 +111,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 +144,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 +193,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 +220,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 +242,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 +300,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 +320,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 +372,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 +398,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 +475,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"
}

View File

@@ -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",
@@ -51,8 +56,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 +65,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 +112,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 +170,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 +192,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 +235,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 +267,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 +296,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 +313,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 +365,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 +391,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 +471,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"
}

View File

@@ -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",
@@ -52,6 +57,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 +67,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 +116,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 +173,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 +198,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 +241,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 +267,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 +304,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 +319,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 +371,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 +397,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 +475,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

View File

@@ -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)

View File

@@ -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

View File

@@ -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
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,14 @@ 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
@@ -1224,6 +1244,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 +1282,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 +1492,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 +1503,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 +2073,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}")

View File

@@ -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;
}

View File

@@ -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,

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,3 @@
{
"version": "2.4.0.1"
"version": "2.6.1.5"
}

View File

@@ -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 (