mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-08 02:37:44 +02:00
Sync from development - prepare for v0.2.2
This commit is contained in:
198
jackify/backend/services/vnv_integration_helper.py
Normal file
198
jackify/backend/services/vnv_integration_helper.py
Normal file
@@ -0,0 +1,198 @@
|
||||
"""
|
||||
VNV Integration Helper
|
||||
|
||||
Helper functions to integrate VNV post-install automation into modlist workflows.
|
||||
Handles detection, confirmation, and execution for:
|
||||
- Install Modlist
|
||||
- Configure New Modlist
|
||||
- Configure Existing Modlist
|
||||
"""
|
||||
|
||||
import logging
|
||||
import configparser
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Optional, Callable, Tuple
|
||||
|
||||
from .vnv_post_install_service import VNVPostInstallService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _parse_bytearray_value(value: str) -> str:
|
||||
"""
|
||||
Parse Qt @ByteArray format to extract the actual string value.
|
||||
|
||||
Format: @ByteArray(Viva New Vegas Extended)
|
||||
Returns: Viva New Vegas Extended
|
||||
"""
|
||||
match = re.match(r'@ByteArray\((.*)\)', value)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return value
|
||||
|
||||
|
||||
def _check_modorganizer_ini_profile(modlist_install_location: Path) -> bool:
|
||||
"""
|
||||
Check ModOrganizer.ini for VNV profile names.
|
||||
|
||||
Args:
|
||||
modlist_install_location: Path to modlist installation directory
|
||||
|
||||
Returns:
|
||||
True if selected_profile is "Viva New Vegas" or "Viva New Vegas Extended"
|
||||
"""
|
||||
try:
|
||||
mo_ini_path = modlist_install_location / "ModOrganizer.ini"
|
||||
if not mo_ini_path.exists():
|
||||
logger.debug(f"ModOrganizer.ini not found at {mo_ini_path}")
|
||||
return False
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
# Read with UTF-8-sig to handle BOM
|
||||
config.read(mo_ini_path, encoding='utf-8-sig')
|
||||
|
||||
if 'General' not in config:
|
||||
logger.debug("No [General] section in ModOrganizer.ini")
|
||||
return False
|
||||
|
||||
selected_profile_raw = config.get('General', 'selected_profile', fallback='')
|
||||
if not selected_profile_raw:
|
||||
logger.debug("No selected_profile in ModOrganizer.ini")
|
||||
return False
|
||||
|
||||
# Parse @ByteArray format
|
||||
selected_profile = _parse_bytearray_value(selected_profile_raw)
|
||||
logger.debug(f"Found selected_profile: {selected_profile}")
|
||||
|
||||
# Check if it's one of the VNV profiles
|
||||
vnv_profiles = ["Viva New Vegas", "Viva New Vegas Extended"]
|
||||
return selected_profile in vnv_profiles
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Error checking ModOrganizer.ini for VNV profile: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def should_offer_vnv_automation(modlist_name: str, modlist_install_location: Optional[Path] = None) -> bool:
|
||||
"""
|
||||
Check if VNV automation should be offered for this modlist.
|
||||
|
||||
Detection methods (in order of reliability):
|
||||
1. Check ModOrganizer.ini selected_profile (most reliable)
|
||||
2. Check modlist name for VNV patterns
|
||||
|
||||
Args:
|
||||
modlist_name: Name of the modlist
|
||||
modlist_install_location: Optional path to modlist installation directory
|
||||
|
||||
Returns:
|
||||
True if VNV automation should be offered
|
||||
"""
|
||||
# Method 1: Check ModOrganizer.ini profile (most reliable)
|
||||
if modlist_install_location:
|
||||
if _check_modorganizer_ini_profile(modlist_install_location):
|
||||
logger.info(f"VNV detected via ModOrganizer.ini profile in {modlist_install_location}")
|
||||
return True
|
||||
|
||||
# Method 2: Check modlist name patterns
|
||||
modlist_name_lower = modlist_name.lower()
|
||||
vnv_patterns = [
|
||||
"viva new vegas",
|
||||
"vnv", # Common abbreviation
|
||||
"viva new vegas extended"
|
||||
]
|
||||
|
||||
for pattern in vnv_patterns:
|
||||
if pattern in modlist_name_lower:
|
||||
logger.info(f"VNV detected via name pattern '{pattern}' in '{modlist_name}'")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def run_vnv_automation_if_applicable(
|
||||
modlist_name: str,
|
||||
modlist_install_location: Path,
|
||||
game_root: Path,
|
||||
ttw_installer_path: Optional[Path] = None,
|
||||
progress_callback: Optional[Callable[[str], None]] = None,
|
||||
manual_file_callback: Optional[Callable[[str, str], Optional[Path]]] = None,
|
||||
confirmation_callback: Optional[Callable[[str], bool]] = None
|
||||
) -> Tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Check if VNV automation should run, get user confirmation, and execute if confirmed.
|
||||
|
||||
Args:
|
||||
modlist_name: Name of the installed modlist
|
||||
modlist_install_location: Path to modlist installation
|
||||
game_root: Path to game root directory
|
||||
ttw_installer_path: Optional path to TTW_Linux_Installer (for BSA decompression)
|
||||
progress_callback: Optional callback for progress updates
|
||||
manual_file_callback: Optional callback for manual file selection (non-Premium)
|
||||
confirmation_callback: Optional callback for user confirmation
|
||||
Takes description string, returns True if user confirms
|
||||
|
||||
Returns:
|
||||
Tuple of (automation_was_run: bool, error_message: Optional[str])
|
||||
"""
|
||||
try:
|
||||
# Check if this is VNV (pass install location for ModOrganizer.ini check)
|
||||
if not should_offer_vnv_automation(modlist_name, modlist_install_location):
|
||||
logger.debug(f"Modlist '{modlist_name}' does not require VNV automation")
|
||||
return False, None
|
||||
|
||||
logger.info(f"VNV detected: {modlist_name}")
|
||||
|
||||
# Initialize service
|
||||
vnv_service = VNVPostInstallService(
|
||||
modlist_install_location=modlist_install_location,
|
||||
game_root=game_root,
|
||||
ttw_installer_path=ttw_installer_path
|
||||
)
|
||||
|
||||
# Check what's already done
|
||||
completed = vnv_service.check_already_completed()
|
||||
# Only skip if ALL three steps are completed
|
||||
if completed['root_mods'] and completed['4gb_patch'] and completed['bsa_decompressed']:
|
||||
logger.info("VNV automation steps already completed")
|
||||
if progress_callback:
|
||||
progress_callback("VNV post-install steps already completed")
|
||||
return False, None
|
||||
|
||||
# Get confirmation from user (required)
|
||||
if not confirmation_callback:
|
||||
logger.error("VNV automation requires confirmation_callback")
|
||||
return False, "VNV automation requires user confirmation"
|
||||
|
||||
if confirmation_callback:
|
||||
description = vnv_service.get_automation_description()
|
||||
if not confirmation_callback(description):
|
||||
logger.info("User declined VNV automation")
|
||||
if progress_callback:
|
||||
progress_callback("VNV automation skipped by user")
|
||||
return False, None
|
||||
|
||||
# Run automation
|
||||
logger.info("Starting VNV post-install automation")
|
||||
if progress_callback:
|
||||
progress_callback("Running VNV post-install automation...")
|
||||
|
||||
success, message = vnv_service.run_all_steps(
|
||||
progress_callback=progress_callback,
|
||||
manual_file_callback=manual_file_callback
|
||||
)
|
||||
|
||||
if success:
|
||||
logger.info(f"VNV automation completed: {message}")
|
||||
if progress_callback:
|
||||
progress_callback(f"VNV automation: {message}")
|
||||
return True, None
|
||||
else:
|
||||
logger.error(f"VNV automation failed: {message}")
|
||||
return True, message
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"VNV automation error: {str(e)}"
|
||||
logger.error(error_msg, exc_info=True)
|
||||
return True, error_msg
|
||||
Reference in New Issue
Block a user