mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-07 21:37:45 +02:00
Sync from development - prepare for v0.5.0.1
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
# Jackify Changelog
|
# Jackify Changelog
|
||||||
|
|
||||||
|
## v0.5.0.1 - Hotfix
|
||||||
|
**Release Date:** 13/03/26
|
||||||
|
|
||||||
|
- Fixed Proton prefix creation failing for users who previously had Flatpak Steam installed but have since switched to native Steam.
|
||||||
|
- Fixed Configure Existing Modlist mangling binary and working directory paths for modlists using a `StockGame` folder (no space variant).
|
||||||
|
|
||||||
## v0.5.0 - Non-Premium Support, Modlist Update Handling and Overall Reliability Improvements
|
## v0.5.0 - Non-Premium Support, Modlist Update Handling and Overall Reliability Improvements
|
||||||
**Release Date:** 13/03/26
|
**Release Date:** 13/03/26
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing
|
|||||||
Wabbajack modlists natively on Linux systems.
|
Wabbajack modlists natively on Linux systems.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "0.5.0"
|
__version__ = "0.5.0.1"
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class PathHandlerGameMixin:
|
|||||||
return False
|
return False
|
||||||
modlist_path = Path(self.modlist_dir)
|
modlist_path = Path(self.modlist_dir)
|
||||||
preferred_order = [
|
preferred_order = [
|
||||||
"Stock Game", "STOCK GAME", "Skyrim Stock", "Stock Game Folder",
|
"Stock Game", "StockGame", "STOCK GAME", "Skyrim Stock", "Stock Game Folder",
|
||||||
"Stock Folder", Path("root/Skyrim Special Edition"), "Game Root"
|
"Stock Folder", Path("root/Skyrim Special Edition"), "Game Root"
|
||||||
]
|
]
|
||||||
found_path = None
|
found_path = None
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ TARGET_EXECUTABLES_LOWER = [
|
|||||||
"skse64_loader.exe", "f4se_loader.exe", "nvse_loader.exe", "obse_loader.exe",
|
"skse64_loader.exe", "f4se_loader.exe", "nvse_loader.exe", "obse_loader.exe",
|
||||||
"sfse_loader.exe", "obse64_loader.exe", "falloutnv.exe"
|
"sfse_loader.exe", "obse64_loader.exe", "falloutnv.exe"
|
||||||
]
|
]
|
||||||
STOCK_GAME_FOLDERS = ["Stock Game", "Game Root", "Stock Folder", "Skyrim Stock"]
|
STOCK_GAME_FOLDERS = ["Stock Game", "StockGame", "Game Root", "Stock Folder", "Skyrim Stock"]
|
||||||
SDCARD_PREFIX = '/run/media/mmcblk0p1/'
|
SDCARD_PREFIX = '/run/media/mmcblk0p1/'
|
||||||
|
|
||||||
|
|
||||||
@@ -433,10 +433,16 @@ class PathHandlerMO2Mixin:
|
|||||||
if "/mods/" in cleaned_value:
|
if "/mods/" in cleaned_value:
|
||||||
idx = cleaned_value.index("/mods/")
|
idx = cleaned_value.index("/mods/")
|
||||||
rel_path = cleaned_value[idx:].lstrip('/')
|
rel_path = cleaned_value[idx:].lstrip('/')
|
||||||
|
elif existing_game_path:
|
||||||
|
rel_path = None
|
||||||
|
game_path_base = existing_game_path
|
||||||
else:
|
else:
|
||||||
rel_path = exe_name
|
rel_path = exe_name
|
||||||
processed_modlist_path = self._strip_sdcard_path_prefix(modlist_dir_path) if modlist_sdcard else str(modlist_dir_path)
|
if rel_path is not None:
|
||||||
new_binary_path = f"{drive_prefix}/{processed_modlist_path}/{rel_path}".replace('\\', '/').replace('//', '/')
|
processed_modlist_path = self._strip_sdcard_path_prefix(modlist_dir_path) if modlist_sdcard else str(modlist_dir_path)
|
||||||
|
new_binary_path = f"{drive_prefix}/{processed_modlist_path}/{rel_path}".replace('\\', '/').replace('//', '/')
|
||||||
|
else:
|
||||||
|
new_binary_path = f"{drive_prefix}/{game_path_base}/{exe_name}".replace('\\', '/').replace('//', '/')
|
||||||
formatted_binary_path = PathHandlerMO2Mixin._format_binary_for_mo2(new_binary_path)
|
formatted_binary_path = PathHandlerMO2Mixin._format_binary_for_mo2(new_binary_path)
|
||||||
if '"' in formatted_binary_path:
|
if '"' in formatted_binary_path:
|
||||||
formatted_binary_path = formatted_binary_path.replace('"', '')
|
formatted_binary_path = formatted_binary_path.replace('"', '')
|
||||||
|
|||||||
@@ -5,12 +5,56 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import re
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class PrefixCreationMixin:
|
class PrefixCreationMixin:
|
||||||
"""Mixin providing prefix creation methods for AutomatedPrefixService."""
|
"""Mixin providing prefix creation methods for AutomatedPrefixService."""
|
||||||
|
|
||||||
|
def _get_preferred_steam_root_and_type(self) -> tuple[Optional[Path], Optional[str]]:
|
||||||
|
"""Resolve the active Steam root/type using the shared v0.5 selector."""
|
||||||
|
from jackify.shared.steam_utils import (
|
||||||
|
STEAM_PREFERENCE_AUTO,
|
||||||
|
resolve_preferred_steam_installation,
|
||||||
|
)
|
||||||
|
from ..handlers.config_handler import ConfigHandler
|
||||||
|
|
||||||
|
preference = STEAM_PREFERENCE_AUTO
|
||||||
|
try:
|
||||||
|
preference = ConfigHandler().get("steam_install_preference", STEAM_PREFERENCE_AUTO)
|
||||||
|
except Exception:
|
||||||
|
logger.debug("Could not read steam_install_preference; falling back to auto", exc_info=True)
|
||||||
|
|
||||||
|
preferred_type, preferred_root = resolve_preferred_steam_installation(preference)
|
||||||
|
return preferred_root, preferred_type
|
||||||
|
|
||||||
|
def _get_library_roots_for_steam_root(self, steam_root: Path) -> list[Path]:
|
||||||
|
"""
|
||||||
|
Read library roots for one chosen Steam install only.
|
||||||
|
|
||||||
|
This avoids mixing native and Flatpak libraries in dual-install environments.
|
||||||
|
"""
|
||||||
|
roots: list[Path] = [steam_root]
|
||||||
|
vdf_path = steam_root / "config" / "libraryfolders.vdf"
|
||||||
|
if not vdf_path.is_file():
|
||||||
|
return roots
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = vdf_path.read_text(encoding="utf-8", errors="ignore")
|
||||||
|
for match in re.finditer(r'"path"\s*"([^"]+)"', text):
|
||||||
|
raw_path = match.group(1).replace("\\\\", "\\")
|
||||||
|
lib_root = Path(raw_path).expanduser()
|
||||||
|
try:
|
||||||
|
resolved = lib_root.resolve()
|
||||||
|
except (OSError, RuntimeError):
|
||||||
|
resolved = lib_root
|
||||||
|
if resolved not in roots:
|
||||||
|
roots.append(resolved)
|
||||||
|
except Exception:
|
||||||
|
logger.debug("Failed reading libraryfolders.vdf for %s", steam_root, exc_info=True)
|
||||||
|
return roots
|
||||||
|
|
||||||
def _get_compatdata_path_for_appid(self, appid: int) -> Optional[Path]:
|
def _get_compatdata_path_for_appid(self, appid: int) -> Optional[Path]:
|
||||||
"""
|
"""
|
||||||
Get the compatdata path for a given AppID.
|
Get the compatdata path for a given AppID.
|
||||||
@@ -31,13 +75,11 @@ class PrefixCreationMixin:
|
|||||||
if compatdata_path:
|
if compatdata_path:
|
||||||
return compatdata_path
|
return compatdata_path
|
||||||
|
|
||||||
# Prefix doesn't exist yet - determine where to create it from libraryfolders.vdf
|
# Prefix doesn't exist yet - derive it from the selected active Steam root,
|
||||||
library_paths = PathHandler.get_all_steam_library_paths()
|
# not from a mixed native/Flatpak library list.
|
||||||
if library_paths:
|
preferred_root, _preferred_type = self._get_preferred_steam_root_and_type()
|
||||||
# Use the first library (typically the default library)
|
if preferred_root:
|
||||||
# Construct compatdata path: library_path/steamapps/compatdata/appid
|
compatdata_base = preferred_root / "steamapps" / "compatdata"
|
||||||
first_library = library_paths[0]
|
|
||||||
compatdata_base = first_library / "steamapps" / "compatdata"
|
|
||||||
return compatdata_base / str(appid)
|
return compatdata_base / str(appid)
|
||||||
|
|
||||||
# Only fallback if VDF parsing completely fails
|
# Only fallback if VDF parsing completely fails
|
||||||
@@ -156,36 +198,24 @@ class PrefixCreationMixin:
|
|||||||
True if successful, False otherwise
|
True if successful, False otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Determine Steam locations based on installation type
|
# Determine Steam locations from the selected active Steam install only.
|
||||||
from ..handlers.path_handler import PathHandler
|
steam_root, steam_type = self._get_preferred_steam_root_and_type()
|
||||||
path_handler = PathHandler()
|
if not steam_root:
|
||||||
all_libraries = path_handler.get_all_steam_library_paths()
|
logger.error("Could not determine active Steam root for prefix creation")
|
||||||
|
return False
|
||||||
# Check if we have Flatpak Steam by looking for .var/app/com.valvesoftware.Steam in library paths
|
|
||||||
is_flatpak_steam = any('.var/app/com.valvesoftware.Steam' in str(lib) for lib in all_libraries)
|
if not steam_root.is_dir():
|
||||||
|
logger.error("Preferred Steam root does not exist: %s", steam_root)
|
||||||
if is_flatpak_steam and all_libraries:
|
return False
|
||||||
# Flatpak Steam: Use the actual library root from libraryfolders.vdf
|
|
||||||
# Compatdata should be in the library root, not the client root
|
compatdata_dir = steam_root / "steamapps" / "compatdata"
|
||||||
flatpak_library_root = all_libraries[0] # Use first library (typically the default)
|
proton_common_dir = steam_root / "steamapps" / "common"
|
||||||
flatpak_client_root = flatpak_library_root.parent.parent / ".steam/steam"
|
logger.info(
|
||||||
|
"Prefix creation using preferred Steam install: type=%s root=%s",
|
||||||
if not flatpak_library_root.is_dir():
|
steam_type or "unknown",
|
||||||
logger.error(
|
steam_root,
|
||||||
f"Flatpak Steam library root does not exist: {flatpak_library_root}"
|
)
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
steam_root = flatpak_client_root if flatpak_client_root.is_dir() else flatpak_library_root
|
|
||||||
# CRITICAL: compatdata must be in the library root, not client root
|
|
||||||
compatdata_dir = flatpak_library_root / "steamapps/compatdata"
|
|
||||||
proton_common_dir = flatpak_library_root / "steamapps/common"
|
|
||||||
else:
|
|
||||||
# Native Steam (or unknown): fall back to legacy ~/.steam/steam layout
|
|
||||||
steam_root = Path.home() / ".steam/steam"
|
|
||||||
compatdata_dir = steam_root / "steamapps/compatdata"
|
|
||||||
proton_common_dir = steam_root / "steamapps/common"
|
|
||||||
|
|
||||||
# Ensure compatdata root exists and is a directory we actually want to use
|
# Ensure compatdata root exists and is a directory we actually want to use
|
||||||
if not compatdata_dir.is_dir():
|
if not compatdata_dir.is_dir():
|
||||||
logger.error(f"Compatdata root does not exist: {compatdata_dir}. Aborting prefix creation.")
|
logger.error(f"Compatdata root does not exist: {compatdata_dir}. Aborting prefix creation.")
|
||||||
@@ -256,4 +286,3 @@ class PrefixCreationMixin:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error creating prefix: {e}")
|
logger.error(f"Error creating prefix: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ exit"""
|
|||||||
def get_prefix_path(self, appid: int) -> Optional[Path]:
|
def get_prefix_path(self, appid: int) -> Optional[Path]:
|
||||||
"""
|
"""
|
||||||
Get the path to the Proton prefix for the given AppID.
|
Get the path to the Proton prefix for the given AppID.
|
||||||
Uses same Flatpak detection as create_prefix_with_proton_wrapper.
|
Uses the same preferred Steam install selection as create_prefix_with_proton_wrapper.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
appid: The AppID (unsigned, positive number)
|
appid: The AppID (unsigned, positive number)
|
||||||
@@ -381,20 +381,11 @@ exit"""
|
|||||||
Returns:
|
Returns:
|
||||||
Path to the prefix directory, or None if not found
|
Path to the prefix directory, or None if not found
|
||||||
"""
|
"""
|
||||||
from ..handlers.path_handler import PathHandler
|
steam_root, _steam_type = self._get_preferred_steam_root_and_type()
|
||||||
path_handler = PathHandler()
|
if not steam_root:
|
||||||
all_libraries = path_handler.get_all_steam_library_paths()
|
return None
|
||||||
|
|
||||||
# Check if Flatpak Steam
|
compatdata_dir = steam_root / "steamapps" / "compatdata"
|
||||||
is_flatpak_steam = any('.var/app/com.valvesoftware.Steam' in str(lib) for lib in all_libraries)
|
|
||||||
|
|
||||||
if is_flatpak_steam and all_libraries:
|
|
||||||
# Flatpak Steam: use first library root
|
|
||||||
library_root = all_libraries[0]
|
|
||||||
compatdata_dir = library_root / "steamapps/compatdata"
|
|
||||||
else:
|
|
||||||
# Native Steam
|
|
||||||
compatdata_dir = Path.home() / ".steam/steam/steamapps/compatdata"
|
|
||||||
|
|
||||||
# Ensure we use the absolute value (unsigned AppID)
|
# Ensure we use the absolute value (unsigned AppID)
|
||||||
prefix_dir = compatdata_dir / str(abs(appid))
|
prefix_dir = compatdata_dir / str(abs(appid))
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ class UpdateService:
|
|||||||
if nexus_url:
|
if nexus_url:
|
||||||
download_url = nexus_url
|
download_url = nexus_url
|
||||||
update_source = "nexus"
|
update_source = "nexus"
|
||||||
logger.debug(f"UPD-1001 update_source_selected source=nexus version={latest_version}")
|
logger.info("Update source: Nexus CDN (version %s)", latest_version)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"UPD-1001 update_source_selected source=github version={latest_version}")
|
logger.info("Update source: GitHub Releases (version %s)", latest_version)
|
||||||
|
|
||||||
# Determine if this is a delta update
|
# Determine if this is a delta update
|
||||||
is_delta = '.delta' in download_url or 'delta' in download_url.lower()
|
is_delta = '.delta' in download_url or 'delta' in download_url.lower()
|
||||||
@@ -167,7 +167,7 @@ class UpdateService:
|
|||||||
auth_service = NexusAuthService()
|
auth_service = NexusAuthService()
|
||||||
token = auth_service.get_auth_token()
|
token = auth_service.get_auth_token()
|
||||||
if not token:
|
if not token:
|
||||||
logger.debug("UPD-1002 nexus_lookup_skipped reason=missing_auth_token")
|
logger.info("Nexus update lookup skipped: no auth token")
|
||||||
return None
|
return None
|
||||||
auth_method = auth_service.get_auth_method()
|
auth_method = auth_service.get_auth_method()
|
||||||
is_oauth = auth_method == "oauth"
|
is_oauth = auth_method == "oauth"
|
||||||
@@ -175,7 +175,7 @@ class UpdateService:
|
|||||||
from jackify.backend.services.nexus_premium_service import NexusPremiumService
|
from jackify.backend.services.nexus_premium_service import NexusPremiumService
|
||||||
is_premium, _ = NexusPremiumService().check_premium_status(token, is_oauth=is_oauth)
|
is_premium, _ = NexusPremiumService().check_premium_status(token, is_oauth=is_oauth)
|
||||||
if not is_premium:
|
if not is_premium:
|
||||||
logger.debug("UPD-1002 nexus_lookup_skipped reason=not_premium")
|
logger.info("Nexus update lookup skipped: not Premium")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
auth_headers = {"Accept": "application/json"}
|
auth_headers = {"Accept": "application/json"}
|
||||||
@@ -201,7 +201,7 @@ class UpdateService:
|
|||||||
match = match or f
|
match = match or f
|
||||||
|
|
||||||
if match is None:
|
if match is None:
|
||||||
logger.debug(f"UPD-1002 nexus_lookup_skipped reason=version_not_on_nexus version={target_version}")
|
logger.info("Nexus update lookup: version %s not found on Nexus", target_version)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
nexus_file_id = match["file_id"]
|
nexus_file_id = match["file_id"]
|
||||||
@@ -212,11 +212,11 @@ class UpdateService:
|
|||||||
if isinstance(links, list) and links:
|
if isinstance(links, list) and links:
|
||||||
cdn_url = links[0].get("URI")
|
cdn_url = links[0].get("URI")
|
||||||
if cdn_url:
|
if cdn_url:
|
||||||
logger.debug(f"UPD-1003 nexus_lookup_success file_id={nexus_file_id} version={target_version}")
|
logger.info("Nexus update CDN link obtained for version %s (file_id=%s)", target_version, nexus_file_id)
|
||||||
return cdn_url
|
return cdn_url
|
||||||
logger.debug("UPD-1002 nexus_lookup_skipped reason=empty_download_links")
|
logger.info("Nexus update lookup: empty download links for version %s", target_version)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"UPD-1004 nexus_lookup_failed error={e}")
|
logger.info("Nexus update lookup failed, falling back to GitHub: %s", e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _is_newer_version(self, version: str) -> bool:
|
def _is_newer_version(self, version: str) -> bool:
|
||||||
@@ -320,8 +320,13 @@ class UpdateService:
|
|||||||
Path to downloaded file, or None if download failed
|
Path to downloaded file, or None if download failed
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
logger.info(f"Downloading update {update_info.version} (full replacement)")
|
logger.info("Downloading update %s from %s (full replacement)", update_info.version, update_info.source)
|
||||||
return self._download_update_manual(update_info, progress_callback)
|
result = self._download_update_manual(update_info, progress_callback)
|
||||||
|
if result:
|
||||||
|
logger.info("Update download complete: %s from %s -> %s", update_info.version, update_info.source, result)
|
||||||
|
else:
|
||||||
|
logger.error("Update download failed: %s from %s", update_info.version, update_info.source)
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to download update: {e}")
|
logger.error(f"Failed to download update: {e}")
|
||||||
@@ -340,7 +345,7 @@ class UpdateService:
|
|||||||
Path to downloaded file, or None if download failed
|
Path to downloaded file, or None if download failed
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
logger.info(f"Manual download of update {update_info.version} from {update_info.download_url}")
|
logger.info("Downloading update %s from %s (%s)", update_info.version, update_info.source, update_info.download_url)
|
||||||
|
|
||||||
response = requests.get(update_info.download_url, stream=True)
|
response = requests.get(update_info.download_url, stream=True)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
@@ -367,7 +372,7 @@ class UpdateService:
|
|||||||
# Make executable
|
# Make executable
|
||||||
temp_file.chmod(0o755)
|
temp_file.chmod(0o755)
|
||||||
|
|
||||||
logger.info(f"Manual update downloaded successfully to {temp_file}")
|
logger.info("Update downloaded successfully: %s from %s -> %s", update_info.version, update_info.source, temp_file)
|
||||||
return temp_file
|
return temp_file
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -397,8 +402,7 @@ class UpdateService:
|
|||||||
helper_script = self._create_update_helper(current_appimage, new_appimage_path)
|
helper_script = self._create_update_helper(current_appimage, new_appimage_path)
|
||||||
|
|
||||||
if helper_script:
|
if helper_script:
|
||||||
# Launch helper script and exit
|
logger.info("Applying update: replacing %s with %s", current_appimage, new_appimage_path)
|
||||||
logger.info("Launching update helper and exiting")
|
|
||||||
subprocess.Popen(['nohup', 'bash', str(helper_script)],
|
subprocess.Popen(['nohup', 'bash', str(helper_script)],
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.DEVNULL)
|
stderr=subprocess.DEVNULL)
|
||||||
|
|||||||
@@ -46,6 +46,22 @@ class ConfigureExistingModlistScreen(
|
|||||||
):
|
):
|
||||||
resize_request = Signal(str)
|
resize_request = Signal(str)
|
||||||
|
|
||||||
|
def _park_thread(self, thread, signal_names=None):
|
||||||
|
"""Disconnect a running thread from this screen and keep it alive until it finishes."""
|
||||||
|
if thread is None:
|
||||||
|
return None
|
||||||
|
signal_names = signal_names or []
|
||||||
|
for signal_name in signal_names:
|
||||||
|
try:
|
||||||
|
getattr(thread, signal_name).disconnect()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if not hasattr(self, "_parked_threads"):
|
||||||
|
self._parked_threads = []
|
||||||
|
self._parked_threads.append(thread)
|
||||||
|
self._parked_threads = [t for t in self._parked_threads if getattr(t, "isRunning", lambda: False)()]
|
||||||
|
return None
|
||||||
|
|
||||||
def cleanup_processes(self):
|
def cleanup_processes(self):
|
||||||
"""Clean up any running processes when the window closes or is cancelled"""
|
"""Clean up any running processes when the window closes or is cancelled"""
|
||||||
if hasattr(self, 'file_progress_list'):
|
if hasattr(self, 'file_progress_list'):
|
||||||
@@ -55,13 +71,11 @@ class ConfigureExistingModlistScreen(
|
|||||||
for attr_name, value in list(vars(self).items()):
|
for attr_name, value in list(vars(self).items()):
|
||||||
try:
|
try:
|
||||||
if isinstance(value, QThread) and value.isRunning():
|
if isinstance(value, QThread) and value.isRunning():
|
||||||
try:
|
signal_names = []
|
||||||
value.finished_signal.disconnect()
|
for candidate in ("finished_signal", "progress_update", "configuration_complete", "error_occurred"):
|
||||||
except Exception:
|
if hasattr(value, candidate):
|
||||||
pass
|
signal_names.append(candidate)
|
||||||
value.terminate()
|
setattr(self, attr_name, self._park_thread(value, signal_names))
|
||||||
value.wait(2000)
|
|
||||||
setattr(self, attr_name, None)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -96,13 +110,9 @@ class ConfigureExistingModlistScreen(
|
|||||||
super().hideEvent(event)
|
super().hideEvent(event)
|
||||||
if self._shortcut_loader is not None:
|
if self._shortcut_loader is not None:
|
||||||
if self._shortcut_loader.isRunning():
|
if self._shortcut_loader.isRunning():
|
||||||
try:
|
self._shortcut_loader = self._park_thread(self._shortcut_loader, ["finished_signal", "error_signal"])
|
||||||
self._shortcut_loader.finished_signal.disconnect()
|
else:
|
||||||
except Exception:
|
self._shortcut_loader = None
|
||||||
pass
|
|
||||||
self._shortcut_loader.terminate()
|
|
||||||
self._shortcut_loader.wait(2000)
|
|
||||||
self._shortcut_loader = None
|
|
||||||
|
|
||||||
def on_configuration_complete(self, success, message, modlist_name, enb_detected=False):
|
def on_configuration_complete(self, success, message, modlist_name, enb_detected=False):
|
||||||
"""Handle configuration completion"""
|
"""Handle configuration completion"""
|
||||||
@@ -226,12 +236,8 @@ class ConfigureExistingModlistScreen(
|
|||||||
|
|
||||||
# Clean up config thread if running
|
# Clean up config thread if running
|
||||||
if hasattr(self, 'config_thread') and self.config_thread and self.config_thread.isRunning():
|
if hasattr(self, 'config_thread') and self.config_thread and self.config_thread.isRunning():
|
||||||
logger.debug("DEBUG: Terminating ConfigurationThread")
|
logger.debug("DEBUG: Parking ConfigurationThread")
|
||||||
try:
|
self.config_thread = self._park_thread(
|
||||||
self.config_thread.progress_update.disconnect()
|
self.config_thread,
|
||||||
self.config_thread.configuration_complete.disconnect()
|
["progress_update", "configuration_complete", "error_occurred"],
|
||||||
self.config_thread.error_occurred.disconnect()
|
)
|
||||||
except (RuntimeError, TypeError):
|
|
||||||
pass
|
|
||||||
self.config_thread.terminate()
|
|
||||||
self.config_thread.wait(2000) # Wait up to 2 seconds
|
|
||||||
|
|||||||
@@ -72,14 +72,20 @@ class ConfigureExistingModlistShortcutsMixin:
|
|||||||
# GC'd while still running (which would cause Qt to abort).
|
# GC'd while still running (which would cause Qt to abort).
|
||||||
if self._shortcut_loader is not None:
|
if self._shortcut_loader is not None:
|
||||||
if self._shortcut_loader.isRunning():
|
if self._shortcut_loader.isRunning():
|
||||||
try:
|
if hasattr(self, '_park_thread'):
|
||||||
self._shortcut_loader.finished_signal.disconnect()
|
self._park_thread(self._shortcut_loader, ["finished_signal", "error_signal"])
|
||||||
except Exception:
|
else:
|
||||||
pass
|
try:
|
||||||
self._shortcut_loader.terminate()
|
self._shortcut_loader.finished_signal.disconnect()
|
||||||
if not hasattr(self, '_old_loaders'):
|
except Exception:
|
||||||
self._old_loaders = []
|
pass
|
||||||
self._old_loaders.append(self._shortcut_loader)
|
try:
|
||||||
|
self._shortcut_loader.error_signal.disconnect()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if not hasattr(self, '_old_loaders'):
|
||||||
|
self._old_loaders = []
|
||||||
|
self._old_loaders.append(self._shortcut_loader)
|
||||||
self._shortcut_loader = None
|
self._shortcut_loader = None
|
||||||
|
|
||||||
# Purge finished threads from the holding list
|
# Purge finished threads from the holding list
|
||||||
@@ -117,4 +123,3 @@ class ConfigureExistingModlistShortcutsMixin:
|
|||||||
self.shortcut_combo.clear()
|
self.shortcut_combo.clear()
|
||||||
self.shortcut_combo.setEnabled(True)
|
self.shortcut_combo.setEnabled(True)
|
||||||
self.shortcut_combo.addItem("Error loading modlists - please try again")
|
self.shortcut_combo.addItem("Error loading modlists - please try again")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user