1
0
forked from Mirrors/RGSX

symlink feature

This commit is contained in:
Sylvain Kraisin
2025-08-13 13:27:37 +02:00
parent 0ade44babd
commit 9b2dcb2b82
9 changed files with 199 additions and 9 deletions

77
SYMLINK_FEATURE.md Normal file
View File

@@ -0,0 +1,77 @@
# Symlink Option Feature
## Overview
This feature adds a simple toggle option to append the platform folder name to the download path, creating a symlink-friendly structure for external storage.
## How It Works
When the symlink option is **disabled** (default):
- Super Nintendo ROMs download to: `../roms/snes/`
- PlayStation 2 ROMs download to: `../roms/ps2/`
When the symlink option is **enabled**:
- Super Nintendo ROMs download to: `../roms/snes/snes/`
- PlayStation 2 ROMs download to: `../roms/ps2/ps2/`
This allows users to create symlinks from the platform folder to external storage locations.
## Usage
1. Open the pause menu (P key or Start button)
2. Navigate to "Symlink Option" (last option in the menu)
3. Press Enter to toggle the option on/off
4. The menu will show the current status: "Symlink option enabled" or "Symlink option disabled"
## Implementation Details
### Files Added
- `symlink_settings.py` - Core functionality for managing the symlink option
### Files Modified
- `display.py` - Added symlink option to pause menu with dynamic status display
- `controls.py` - Added handling for symlink option toggle
- `network.py` - Modified download functions to use symlink paths when enabled
- Language files - Added translation strings for all supported languages
### Configuration
The symlink setting is stored in `symlink_settings.json` in the save folder:
```json
{
"use_symlink_path": false
}
```
### API Functions
- `get_symlink_option()` - Get current symlink option status
- `set_symlink_option(enabled)` - Enable/disable the symlink option
- `apply_symlink_path(base_path, platform_folder)` - Apply symlink path modification
## Example Use Case
1. Enable the symlink option
2. **Optional**: Create a symlink: `ln -s /external/storage/snes ../roms/snes/snes`
- If you don't create the symlink, the nested directories will be created automatically when you download ROMs
3. Download ROMs - the nested directories (like `../roms/snes/snes/`) will be created automatically if they don't exist
4. Now Super Nintendo ROMs will download to the external storage via the symlink (if created) or to the local nested directory
## Features
- **Simple Toggle**: Easy on/off switch in the pause menu
- **Persistent Settings**: Option is remembered between sessions
- **Multi-language Support**: Full internationalization
- **Backward Compatible**: Disabled by default, doesn't affect existing setups
- **Platform Agnostic**: Works with all platforms automatically
- **Automatic Directory Creation**: Nested directories are created automatically if they don't exist
## Technical Notes
- The option is disabled by default
- Settings are stored in JSON format
- Path modification is applied at download time
- Works with both regular downloads and 1fichier downloads
- No impact on existing ROMs or folder structure
- Missing directories are automatically created using `os.makedirs(dest_dir, exist_ok=True)`

View File

@@ -880,6 +880,14 @@ def handle_controls(event, sources, joystick, screen):
config.confirm_selection = 0
config.needs_redraw = True
logger.debug(f"Passage à confirm_exit depuis pause_menu")
elif config.selected_option == 8: # Symlink option
from symlink_settings import set_symlink_option, get_symlink_option
current_status = get_symlink_option()
success, message = set_symlink_option(not current_status)
config.popup_message = message
config.popup_timer = 3000 if success else 5000
config.needs_redraw = True
logger.info(f"Symlink option {'activée' if not current_status else 'désactivée'} via menu pause")
elif is_input_matched(event, "cancel"):
config.menu_state = validate_menu_state(config.previous_menu_state)
config.needs_redraw = True

View File

@@ -1214,6 +1214,13 @@ def draw_pause_menu(screen, selected_option):
else:
music_option = _("menu_music_disabled")
# Option symlink dynamique
from symlink_settings import get_symlink_option
if get_symlink_option():
symlink_option = _("symlink_option_enabled")
else:
symlink_option = _("symlink_option_disabled")
options = [
_("menu_controls"),
_("menu_remap_controls"),
@@ -1222,7 +1229,8 @@ def draw_pause_menu(screen, selected_option):
_("menu_accessibility"),
_("menu_redownload_cache"),
music_option, # Ici l'option dynamique
_("menu_quit")
_("menu_quit"),
symlink_option
]
menu_width = int(config.screen_width * 0.8)

View File

@@ -186,5 +186,11 @@
"utils_permission_denied": "Berechtigung während der Extraktion verweigert: {0}",
"utils_extraction_failed": "Extraktion fehlgeschlagen: {0}",
"utils_unrar_unavailable": "Befehl unrar nicht verfügbar",
"utils_rar_list_failed": "Fehler beim Auflisten der RAR-Dateien: {0}"
"utils_rar_list_failed": "Fehler beim Auflisten der RAR-Dateien: {0}",
"menu_symlink_option": "Symlink-Option",
"symlink_option_enabled": "Symlink-Option aktiviert",
"symlink_option_disabled": "Symlink-Option deaktiviert",
"symlink_settings_saved_successfully": "Symlink-Einstellungen erfolgreich gespeichert",
"symlink_settings_save_error": "Fehler beim Speichern der Symlink-Einstellungen"
}

View File

@@ -177,5 +177,11 @@
"controls_cancel_back": "Cancel/Back",
"controls_history": "History",
"controls_clear_history": "Clear History",
"controls_filter_search": "Filter/Search"
"controls_filter_search": "Filter/Search",
"menu_symlink_option": "Symlink Option",
"symlink_option_enabled": "Symlink option enabled",
"symlink_option_disabled": "Symlink option disabled",
"symlink_settings_saved_successfully": "Symlink settings saved successfully",
"symlink_settings_save_error": "Error saving symlink settings"
}

View File

@@ -188,5 +188,11 @@
"utils_permission_denied": "Permiso denegado durante la extracción: {0}",
"utils_extraction_failed": "Error en la extracción: {0}",
"utils_unrar_unavailable": "Comando unrar no disponible",
"utils_rar_list_failed": "Error al listar los archivos RAR: {0}"
"utils_rar_list_failed": "Error al listar los archivos RAR: {0}",
"menu_symlink_option": "Opción Symlink",
"symlink_option_enabled": "Opción symlink habilitada",
"symlink_option_disabled": "Opción symlink deshabilitada",
"symlink_settings_saved_successfully": "Configuración symlink guardada con éxito",
"symlink_settings_save_error": "Error al guardar la configuración symlink"
}

View File

@@ -187,5 +187,11 @@
"controls_mapping_title": "Controls configuration",
"controls_mapping_instruction": "Hold for 3s to configure:",
"controls_mapping_waiting": "Waiting for a key or button...",
"controls_mapping_press": "Press a key or button"
"controls_mapping_press": "Press a key or button",
"menu_symlink_option": "Option Symlink",
"symlink_option_enabled": "Option symlink activée",
"symlink_option_disabled": "Option symlink désactivée",
"symlink_settings_saved_successfully": "Paramètres symlink sauvegardés avec succès",
"symlink_settings_save_error": "Erreur lors de la sauvegarde des paramètres symlink"
}

View File

@@ -176,14 +176,19 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
def download_thread():
logger.debug(f"Thread téléchargement démarré pour {url}, task_id={task_id}")
try:
# Use symlink path if enabled
from symlink_settings import apply_symlink_path
dest_dir = None
for platform_dict in config.platform_dicts:
if platform_dict["platform"] == platform:
dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", normalize_platform_name(platform)))
platform_folder = platform_dict.get("folder", normalize_platform_name(platform))
dest_dir = apply_symlink_path(config.ROMS_FOLDER, platform_folder)
logger.debug(f"Répertoire de destination trouvé pour {platform}: {dest_dir}")
break
if not dest_dir:
dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), normalize_platform_name(platform))
platform_folder = normalize_platform_name(platform)
dest_dir = apply_symlink_path(config.ROMS_FOLDER, platform_folder)
os.makedirs(dest_dir, exist_ok=True)
if not os.access(dest_dir, os.W_OK):
@@ -377,14 +382,19 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
try:
link = url.split('&af=')[0]
logger.debug(f"URL nettoyée: {link}")
# Use symlink path if enabled
from symlink_settings import apply_symlink_path
dest_dir = None
for platform_dict in config.platform_dicts:
if platform_dict["platform"] == platform:
dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", normalize_platform_name(platform)))
platform_folder = platform_dict.get("folder", normalize_platform_name(platform))
dest_dir = apply_symlink_path(config.ROMS_FOLDER, platform_folder)
break
if not dest_dir:
logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}")
dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform)
platform_folder = normalize_platform_name(platform)
dest_dir = apply_symlink_path(config.ROMS_FOLDER, platform_folder)
logger.debug(f"Répertoire destination déterminé: {dest_dir}")
logger.debug(f"Vérification répertoire destination: {dest_dir}")

View File

@@ -0,0 +1,63 @@
import os
import json
import logging
import config
from language import _
logger = logging.getLogger(__name__)
# Path for symlink settings
SYMLINK_SETTINGS_PATH = os.path.join(config.SAVE_FOLDER, "symlink_settings.json")
def load_symlink_settings():
"""Load symlink settings from file."""
try:
if os.path.exists(SYMLINK_SETTINGS_PATH):
with open(SYMLINK_SETTINGS_PATH, 'r', encoding='utf-8') as f:
settings = json.load(f)
if not isinstance(settings, dict):
settings = {}
if "use_symlink_path" not in settings:
settings["use_symlink_path"] = False
return settings
except Exception as e:
logger.error(f"Error loading symlink settings: {str(e)}")
# Return default settings (disabled)
return {"use_symlink_path": False}
def save_symlink_settings(settings):
"""Save symlink settings to file."""
try:
os.makedirs(config.SAVE_FOLDER, exist_ok=True)
with open(SYMLINK_SETTINGS_PATH, 'w', encoding='utf-8') as f:
json.dump(settings, f, indent=2)
logger.debug(f"Symlink settings saved: {settings}")
return True
except Exception as e:
logger.error(f"Error saving symlink settings: {str(e)}")
return False
def set_symlink_option(enabled):
"""Enable or disable the symlink option."""
settings = load_symlink_settings()
settings["use_symlink_path"] = enabled
if save_symlink_settings(settings):
return True, _("symlink_settings_saved_successfully")
else:
return False, _("symlink_settings_save_error")
def get_symlink_option():
"""Get current symlink option status."""
settings = load_symlink_settings()
return settings.get("use_symlink_path", False)
def apply_symlink_path(base_path, platform_folder):
"""Apply symlink path modification if enabled."""
if get_symlink_option():
# Append the platform folder name to create symlink path
return os.path.join(base_path, platform_folder, platform_folder)
else:
# Return original path
return os.path.join(base_path, platform_folder)