forked from Mirrors/RGSX
symlink feature
This commit is contained in:
77
SYMLINK_FEATURE.md
Normal file
77
SYMLINK_FEATURE.md
Normal 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)`
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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}")
|
||||
|
||||
63
ports/RGSX/symlink_settings.py
Normal file
63
ports/RGSX/symlink_settings.py
Normal 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)
|
||||
Reference in New Issue
Block a user