mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-01-17 11:37:01 +01:00
Jackify provides native Linux support for Wabbajack modlist installation and management with automated Steam integration and Proton configuration. Key Features: - Almost Native Linux implementation (texconv.exe run via proton) - Automated Steam shortcut creation and Proton prefix management - Both CLI and GUI interfaces, with Steam Deck optimization Supported Games: - Skyrim Special Edition - Fallout 4 - Fallout New Vegas - Oblivion, Starfield, Enderal, and diverse other games Technical Architecture: - Clean separation between frontend and backend services - Powered by jackify-engine 0.3.x for Wabbajack-matching modlist installation
194 lines
9.6 KiB
Python
194 lines
9.6 KiB
Python
"""
|
|
Tuxborn Menu Handler for Jackify CLI Frontend
|
|
Extracted from src.modules.menu_handler.MenuHandler.show_tuxborn_installer_menu()
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from jackify.shared.colors import (
|
|
COLOR_SELECTION, COLOR_RESET, COLOR_INFO, COLOR_PROMPT, COLOR_WARNING
|
|
)
|
|
from jackify.shared.ui_utils import print_jackify_banner
|
|
from jackify.backend.handlers.config_handler import ConfigHandler
|
|
|
|
class TuxbornMenuHandler:
|
|
"""
|
|
Handles the Tuxborn Automatic Installer workflow
|
|
Extracted from legacy MenuHandler class
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = None # Will be set by CLI when needed
|
|
|
|
def show_tuxborn_installer_menu(self, cli_instance):
|
|
"""
|
|
Implements the Tuxborn Automatic Installer workflow.
|
|
Prompts for install path, downloads path, and Nexus API key, then runs the one-shot install from start to finish
|
|
|
|
Args:
|
|
cli_instance: Reference to main CLI instance for access to handlers
|
|
"""
|
|
# Import backend service
|
|
from jackify.backend.core.modlist_operations import ModlistInstallCLI
|
|
|
|
print_jackify_banner()
|
|
print(f"{COLOR_SELECTION}Tuxborn Automatic Installer{COLOR_RESET}")
|
|
print(f"{COLOR_SELECTION}{'-'*32}{COLOR_RESET}")
|
|
print(f"{COLOR_INFO}This will install the Tuxborn modlist using the custom Jackify Install Engine in one automated flow.{COLOR_RESET}")
|
|
print(f"{COLOR_INFO}You will be prompted for the install location, downloads directory, and your Nexus API key.{COLOR_RESET}\n")
|
|
|
|
tuxborn_machineid = "Tuxborn/Tuxborn"
|
|
tuxborn_modlist_name = "Tuxborn"
|
|
|
|
# Prompt for install directory
|
|
print("----------------------------")
|
|
config_handler = ConfigHandler()
|
|
base_install_dir = Path(config_handler.get_modlist_install_base_dir())
|
|
default_install_dir = base_install_dir / "Skyrim" / "Tuxborn"
|
|
print(f"{COLOR_PROMPT}Please enter the directory you wish to use for Tuxborn installation.{COLOR_RESET}")
|
|
print(f"(Default: {default_install_dir})")
|
|
install_dir_result = self._get_directory_path_legacy(
|
|
cli_instance,
|
|
prompt_message=f"{COLOR_PROMPT}Install directory (Enter for default, 'q' to cancel): {COLOR_RESET}",
|
|
default_path=default_install_dir,
|
|
create_if_missing=True,
|
|
no_header=True
|
|
)
|
|
if not install_dir_result:
|
|
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
|
|
input("Press Enter to return to the main menu...")
|
|
return
|
|
if isinstance(install_dir_result, tuple):
|
|
install_dir, _ = install_dir_result # We'll use the path, creation handled by engine or later
|
|
else:
|
|
install_dir = install_dir_result
|
|
|
|
# Prompt for download directory
|
|
print("----------------------------")
|
|
base_download_dir = Path(config_handler.get_modlist_downloads_base_dir())
|
|
default_download_dir = base_download_dir / "Tuxborn"
|
|
print(f"{COLOR_PROMPT}Please enter the directory you wish to use for Tuxborn downloads.{COLOR_RESET}")
|
|
print(f"(Default: {default_download_dir})")
|
|
download_dir_result = self._get_directory_path_legacy(
|
|
cli_instance,
|
|
prompt_message=f"{COLOR_PROMPT}Download directory (Enter for default, 'q' to cancel): {COLOR_RESET}",
|
|
default_path=default_download_dir,
|
|
create_if_missing=True,
|
|
no_header=True
|
|
)
|
|
if not download_dir_result:
|
|
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
|
|
input("Press Enter to return to the main menu...")
|
|
return
|
|
if isinstance(download_dir_result, tuple):
|
|
download_dir, _ = download_dir_result # We'll use the path, creation handled by engine or later
|
|
else:
|
|
download_dir = download_dir_result
|
|
|
|
# Prompt for Nexus API key
|
|
print("----------------------------")
|
|
from jackify.backend.services.api_key_service import APIKeyService
|
|
api_key_service = APIKeyService()
|
|
saved_key = api_key_service.get_saved_api_key()
|
|
api_key = None
|
|
|
|
if saved_key:
|
|
print(f"{COLOR_INFO}A Nexus API Key is already saved.{COLOR_RESET}")
|
|
use_saved = input(f"{COLOR_PROMPT}Use the saved API key? [Y/n]: {COLOR_RESET}").strip().lower()
|
|
if use_saved in ('', 'y', 'yes'):
|
|
api_key = saved_key
|
|
else:
|
|
new_key = input(f"{COLOR_PROMPT}Enter a new Nexus API Key (or press Enter to keep the saved one): {COLOR_RESET}").strip()
|
|
if new_key:
|
|
api_key = new_key
|
|
replace = input(f"{COLOR_PROMPT}Replace the saved key with this one? [y/N]: {COLOR_RESET}").strip().lower()
|
|
if replace == 'y':
|
|
if api_key_service.save_api_key(api_key):
|
|
print(f"{COLOR_INFO}API key saved successfully.{COLOR_RESET}")
|
|
else:
|
|
print(f"{COLOR_WARNING}Failed to save API key. Using for this session only.{COLOR_RESET}")
|
|
else:
|
|
print(f"{COLOR_INFO}Using new key for this session only. Saved key unchanged.{COLOR_RESET}")
|
|
else:
|
|
api_key = saved_key
|
|
else:
|
|
print(f"{COLOR_PROMPT}A Nexus Mods API key is required for downloading mods.{COLOR_RESET}")
|
|
print(f"{COLOR_INFO}You can get your personal key at: {COLOR_SELECTION}https://www.nexusmods.com/users/myaccount?tab=api{COLOR_RESET}")
|
|
print(f"{COLOR_WARNING}Your API Key is NOT saved locally. It is used only for this session unless you choose to save it.{COLOR_RESET}")
|
|
api_key = input(f"{COLOR_PROMPT}Enter Nexus API Key (or 'q' to cancel): {COLOR_RESET}").strip()
|
|
if not api_key or api_key.lower() == 'q':
|
|
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
|
|
input("Press Enter to return to the main menu...")
|
|
return
|
|
save = input(f"{COLOR_PROMPT}Would you like to save this API key for future use? [y/N]: {COLOR_RESET}").strip().lower()
|
|
if save == 'y':
|
|
if api_key_service.save_api_key(api_key):
|
|
print(f"{COLOR_INFO}API key saved successfully.{COLOR_RESET}")
|
|
else:
|
|
print(f"{COLOR_WARNING}Failed to save API key. Using for this session only.{COLOR_RESET}")
|
|
else:
|
|
print(f"{COLOR_INFO}Using API key for this session only. It will not be saved.{COLOR_RESET}")
|
|
|
|
# Context for ModlistInstallCLI
|
|
context = {
|
|
'machineid': tuxborn_machineid,
|
|
'modlist_name': tuxborn_modlist_name, # Will be used for shortcut name
|
|
'install_dir': install_dir_result, # Pass tuple (path, create_flag) or path
|
|
'download_dir': download_dir_result, # Pass tuple (path, create_flag) or path
|
|
'nexus_api_key': api_key,
|
|
'resolution': None
|
|
}
|
|
|
|
modlist_cli = ModlistInstallCLI(self, getattr(cli_instance, 'steamdeck', False))
|
|
|
|
# run_discovery_phase will use context_override, display summary, and ask for confirmation.
|
|
# If user confirms, it returns the context, otherwise None.
|
|
confirmed_context = modlist_cli.run_discovery_phase(context_override=context)
|
|
|
|
if confirmed_context:
|
|
if self.logger:
|
|
self.logger.info("Tuxborn discovery confirmed by user. Proceeding to configuration/installation.")
|
|
# The modlist_cli instance now holds the confirmed context.
|
|
# configuration_phase will use modlist_cli.context
|
|
modlist_cli.configuration_phase()
|
|
# After configuration_phase, messages about success or next steps are handled within it or by _configure_new_modlist
|
|
else:
|
|
if self.logger:
|
|
self.logger.info("Tuxborn discovery/confirmation cancelled or failed.")
|
|
print(f"{COLOR_INFO}Tuxborn installation cancelled or not confirmed.{COLOR_RESET}")
|
|
input(f"{COLOR_PROMPT}Press Enter to return to the main menu...{COLOR_RESET}")
|
|
return
|
|
|
|
def _get_directory_path_legacy(self, cli_instance, prompt_message: str, default_path: Optional[Path],
|
|
create_if_missing: bool = True, no_header: bool = False) -> Optional[Path]:
|
|
"""
|
|
LEGACY BRIDGE: Delegate to legacy menu handler until full backend migration
|
|
|
|
Args:
|
|
cli_instance: Reference to main CLI instance
|
|
prompt_message: The prompt to show user
|
|
default_path: Default path if user presses Enter
|
|
create_if_missing: Whether to create directory if it doesn't exist
|
|
no_header: Whether to skip header display
|
|
|
|
Returns:
|
|
Path object or None if cancelled
|
|
"""
|
|
# LEGACY BRIDGE: Use the original menu handler's method
|
|
if hasattr(cli_instance, 'menu') and hasattr(cli_instance.menu, 'get_directory_path'):
|
|
return cli_instance.menu.get_directory_path(
|
|
prompt_message=prompt_message,
|
|
default_path=default_path,
|
|
create_if_missing=create_if_missing,
|
|
no_header=no_header
|
|
)
|
|
else:
|
|
# Fallback: simple input for now (will be replaced in future phases)
|
|
response = input(prompt_message).strip()
|
|
if response.lower() == 'q':
|
|
return None
|
|
elif response == '':
|
|
return default_path
|
|
else:
|
|
return Path(response) |