Sync from development - prepare for v0.2.0.6

This commit is contained in:
Omni
2025-12-28 18:52:07 +00:00
parent a813236e51
commit 99fb369d5e
50 changed files with 512 additions and 244 deletions

View File

@@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing
Wabbajack modlists natively on Linux systems.
"""
__version__ = "0.2.0.5"
__version__ = "0.2.0.6"

View File

@@ -142,6 +142,11 @@ class OAuthTokenHandler:
"""
try:
from Crypto.Cipher import AES
# Check if MODE_GCM is available (pycryptodome has it, old pycrypto doesn't)
if not hasattr(AES, 'MODE_GCM'):
logger.error("pycryptodome required for token decryption (pycrypto doesn't support MODE_GCM)")
return None
# Derive 32-byte AES key from encryption_key
key = base64.urlsafe_b64decode(self._encryption_key)
@@ -163,6 +168,9 @@ class OAuthTokenHandler:
except ImportError:
logger.error("pycryptodome package not available for token decryption")
return None
except AttributeError:
logger.error("pycryptodome required for token decryption (pycrypto doesn't support MODE_GCM)")
return None
except Exception as e:
logger.error(f"Failed to decrypt data: {e}")
return None

View File

@@ -48,6 +48,9 @@ def get_clean_subprocess_env(extra_env=None):
env = os.environ.copy()
# Save APPDIR before removing it (we need it to find bundled tools)
appdir = env.get('APPDIR')
# Remove AppImage-specific variables that can confuse subprocess calls
# These variables cause subprocesses to be interpreted as new AppImage launches
for key in ['APPIMAGE', 'APPDIR', 'ARGV0', 'OWD']:
@@ -57,10 +60,10 @@ def get_clean_subprocess_env(extra_env=None):
for k in list(env):
if k.startswith('_MEIPASS'):
del env[k]
# Get current PATH - ensure we preserve system paths
current_path = env.get('PATH', '')
# Ensure common system directories are in PATH if not already present
# This is critical for tools like lz4 that might be in /usr/bin, /usr/local/bin, etc.
system_paths = ['/usr/bin', '/usr/local/bin', '/bin', '/sbin', '/usr/sbin']
@@ -68,10 +71,10 @@ def get_clean_subprocess_env(extra_env=None):
for sys_path in system_paths:
if sys_path not in path_parts and os.path.isdir(sys_path):
path_parts.append(sys_path)
# Add bundled tools directory to PATH if running as AppImage
# This ensures lz4, unzip, xz, etc. are available to subprocesses
appdir = env.get('APPDIR')
# Note: appdir was saved before env cleanup above
tools_dir = None
if appdir:

View File

@@ -34,9 +34,6 @@ def is_non_premium_indicator(line: str) -> bool:
if phrase in normalized:
return True
if "nexus" in normalized and "premium" in normalized:
return True
# Manual download + Nexus URL implies premium requirement in current workflows.
if "manual download" in normalized and ("nexusmods.com" in normalized or "nexus mods" in normalized):
return True

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -7,7 +7,7 @@
"targets": {
".NETCoreApp,Version=v8.0": {},
".NETCoreApp,Version=v8.0/linux-x64": {
"jackify-engine/0.4.3": {
"jackify-engine/0.4.4": {
"dependencies": {
"Markdig": "0.40.0",
"Microsoft.Extensions.Configuration.Json": "9.0.1",
@@ -22,16 +22,16 @@
"SixLabors.ImageSharp": "3.1.6",
"System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1",
"Wabbajack.CLI.Builder": "0.4.3",
"Wabbajack.Downloaders.Bethesda": "0.4.3",
"Wabbajack.Downloaders.Dispatcher": "0.4.3",
"Wabbajack.Hashing.xxHash64": "0.4.3",
"Wabbajack.Networking.Discord": "0.4.3",
"Wabbajack.Networking.GitHub": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3",
"Wabbajack.Server.Lib": "0.4.3",
"Wabbajack.Services.OSIntegrated": "0.4.3",
"Wabbajack.VFS": "0.4.3",
"Wabbajack.CLI.Builder": "0.4.4",
"Wabbajack.Downloaders.Bethesda": "0.4.4",
"Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Networking.Discord": "0.4.4",
"Wabbajack.Networking.GitHub": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.Server.Lib": "0.4.4",
"Wabbajack.Services.OSIntegrated": "0.4.4",
"Wabbajack.VFS": "0.4.4",
"MegaApiClient": "1.0.0.0",
"runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.22"
},
@@ -1781,7 +1781,7 @@
}
}
},
"Wabbajack.CLI.Builder/0.4.3": {
"Wabbajack.CLI.Builder/0.4.4": {
"dependencies": {
"Microsoft.Extensions.Configuration.Json": "9.0.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -1791,109 +1791,109 @@
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1",
"Wabbajack.Paths": "0.4.3"
"Wabbajack.Paths": "0.4.4"
},
"runtime": {
"Wabbajack.CLI.Builder.dll": {}
}
},
"Wabbajack.Common/0.4.3": {
"Wabbajack.Common/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.Reactive": "6.0.1",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3"
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4"
},
"runtime": {
"Wabbajack.Common.dll": {}
}
},
"Wabbajack.Compiler/0.4.3": {
"Wabbajack.Compiler/0.4.4": {
"dependencies": {
"F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Dispatcher": "0.4.3",
"Wabbajack.Installer": "0.4.3",
"Wabbajack.VFS": "0.4.3",
"Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Installer": "0.4.4",
"Wabbajack.VFS": "0.4.4",
"ini-parser-netstandard": "2.5.2"
},
"runtime": {
"Wabbajack.Compiler.dll": {}
}
},
"Wabbajack.Compression.BSA/0.4.3": {
"Wabbajack.Compression.BSA/0.4.4": {
"dependencies": {
"K4os.Compression.LZ4.Streams": "1.3.8",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.4.3",
"Wabbajack.DTOs": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.4"
},
"runtime": {
"Wabbajack.Compression.BSA.dll": {}
}
},
"Wabbajack.Compression.Zip/0.4.3": {
"Wabbajack.Compression.Zip/0.4.4": {
"dependencies": {
"Wabbajack.IO.Async": "0.4.3"
"Wabbajack.IO.Async": "0.4.4"
},
"runtime": {
"Wabbajack.Compression.Zip.dll": {}
}
},
"Wabbajack.Configuration/0.4.3": {
"Wabbajack.Configuration/0.4.4": {
"runtime": {
"Wabbajack.Configuration.dll": {}
}
},
"Wabbajack.Downloaders.Bethesda/0.4.3": {
"Wabbajack.Downloaders.Bethesda/0.4.4": {
"dependencies": {
"LibAES-CTR": "1.1.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Networking.BethesdaNet": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.BethesdaNet": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.Bethesda.dll": {}
}
},
"Wabbajack.Downloaders.Dispatcher/0.4.3": {
"Wabbajack.Downloaders.Dispatcher/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Bethesda": "0.4.3",
"Wabbajack.Downloaders.GameFile": "0.4.3",
"Wabbajack.Downloaders.GoogleDrive": "0.4.3",
"Wabbajack.Downloaders.Http": "0.4.3",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Downloaders.Manual": "0.4.3",
"Wabbajack.Downloaders.MediaFire": "0.4.3",
"Wabbajack.Downloaders.Mega": "0.4.3",
"Wabbajack.Downloaders.ModDB": "0.4.3",
"Wabbajack.Downloaders.Nexus": "0.4.3",
"Wabbajack.Downloaders.VerificationCache": "0.4.3",
"Wabbajack.Downloaders.WabbajackCDN": "0.4.3",
"Wabbajack.Networking.WabbajackClientApi": "0.4.3"
"Wabbajack.Downloaders.Bethesda": "0.4.4",
"Wabbajack.Downloaders.GameFile": "0.4.4",
"Wabbajack.Downloaders.GoogleDrive": "0.4.4",
"Wabbajack.Downloaders.Http": "0.4.4",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Downloaders.Manual": "0.4.4",
"Wabbajack.Downloaders.MediaFire": "0.4.4",
"Wabbajack.Downloaders.Mega": "0.4.4",
"Wabbajack.Downloaders.ModDB": "0.4.4",
"Wabbajack.Downloaders.Nexus": "0.4.4",
"Wabbajack.Downloaders.VerificationCache": "0.4.4",
"Wabbajack.Downloaders.WabbajackCDN": "0.4.4",
"Wabbajack.Networking.WabbajackClientApi": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.Dispatcher.dll": {}
}
},
"Wabbajack.Downloaders.GameFile/0.4.3": {
"Wabbajack.Downloaders.GameFile/0.4.4": {
"dependencies": {
"GameFinder.StoreHandlers.EADesktop": "4.5.0",
"GameFinder.StoreHandlers.EGS": "4.5.0",
@@ -1903,360 +1903,360 @@
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.VFS": "0.4.3"
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.VFS": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.GameFile.dll": {}
}
},
"Wabbajack.Downloaders.GoogleDrive/0.4.3": {
"Wabbajack.Downloaders.GoogleDrive/0.4.4": {
"dependencies": {
"HtmlAgilityPack": "1.11.72",
"Microsoft.AspNetCore.Http.Extensions": "2.3.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.4.3",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.GoogleDrive.dll": {}
}
},
"Wabbajack.Downloaders.Http/0.4.3": {
"Wabbajack.Downloaders.Http/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.4.3",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Networking.BethesdaNet": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.BethesdaNet": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.Http.dll": {}
}
},
"Wabbajack.Downloaders.Interfaces/0.4.3": {
"Wabbajack.Downloaders.Interfaces/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Compression.Zip": "0.4.3",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3"
"Wabbajack.Compression.Zip": "0.4.4",
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.Interfaces.dll": {}
}
},
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.3": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.4": {
"dependencies": {
"F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {}
}
},
"Wabbajack.Downloaders.Manual/0.4.3": {
"Wabbajack.Downloaders.Manual/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.Manual.dll": {}
}
},
"Wabbajack.Downloaders.MediaFire/0.4.3": {
"Wabbajack.Downloaders.MediaFire/0.4.4": {
"dependencies": {
"HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.MediaFire.dll": {}
}
},
"Wabbajack.Downloaders.Mega/0.4.3": {
"Wabbajack.Downloaders.Mega/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.Mega.dll": {}
}
},
"Wabbajack.Downloaders.ModDB/0.4.3": {
"Wabbajack.Downloaders.ModDB/0.4.4": {
"dependencies": {
"HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.ModDB.dll": {}
}
},
"Wabbajack.Downloaders.Nexus/0.4.3": {
"Wabbajack.Downloaders.Nexus/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Hashing.xxHash64": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3",
"Wabbajack.Networking.NexusApi": "0.4.3",
"Wabbajack.Paths": "0.4.3"
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Networking.NexusApi": "0.4.4",
"Wabbajack.Paths": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.Nexus.dll": {}
}
},
"Wabbajack.Downloaders.VerificationCache/0.4.3": {
"Wabbajack.Downloaders.VerificationCache/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Stub.System.Data.SQLite.Core.NetStandard": "1.0.119",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3"
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.VerificationCache.dll": {}
}
},
"Wabbajack.Downloaders.WabbajackCDN/0.4.3": {
"Wabbajack.Downloaders.WabbajackCDN/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Microsoft.Toolkit.HighPerformance": "7.1.2",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.RateLimiter": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.RateLimiter": "0.4.4"
},
"runtime": {
"Wabbajack.Downloaders.WabbajackCDN.dll": {}
}
},
"Wabbajack.DTOs/0.4.3": {
"Wabbajack.DTOs/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Hashing.xxHash64": "0.4.3",
"Wabbajack.Paths": "0.4.3"
"Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Paths": "0.4.4"
},
"runtime": {
"Wabbajack.DTOs.dll": {}
}
},
"Wabbajack.FileExtractor/0.4.3": {
"Wabbajack.FileExtractor/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"OMODFramework": "3.0.1",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Compression.BSA": "0.4.3",
"Wabbajack.Hashing.PHash": "0.4.3",
"Wabbajack.Paths": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Compression.BSA": "0.4.4",
"Wabbajack.Hashing.PHash": "0.4.4",
"Wabbajack.Paths": "0.4.4"
},
"runtime": {
"Wabbajack.FileExtractor.dll": {}
}
},
"Wabbajack.Hashing.PHash/0.4.3": {
"Wabbajack.Hashing.PHash/0.4.4": {
"dependencies": {
"BCnEncoder.Net.ImageSharp": "1.1.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Shipwreck.Phash": "0.5.0",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.4.3",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Paths": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4"
},
"runtime": {
"Wabbajack.Hashing.PHash.dll": {}
}
},
"Wabbajack.Hashing.xxHash64/0.4.3": {
"Wabbajack.Hashing.xxHash64/0.4.4": {
"dependencies": {
"Wabbajack.Paths": "0.4.3",
"Wabbajack.RateLimiter": "0.4.3"
"Wabbajack.Paths": "0.4.4",
"Wabbajack.RateLimiter": "0.4.4"
},
"runtime": {
"Wabbajack.Hashing.xxHash64.dll": {}
}
},
"Wabbajack.Installer/0.4.3": {
"Wabbajack.Installer/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"Octopus.Octodiff": "2.0.548",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Downloaders.Dispatcher": "0.4.3",
"Wabbajack.Downloaders.GameFile": "0.4.3",
"Wabbajack.FileExtractor": "0.4.3",
"Wabbajack.Networking.WabbajackClientApi": "0.4.3",
"Wabbajack.Paths": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3",
"Wabbajack.VFS": "0.4.3",
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Downloaders.GameFile": "0.4.4",
"Wabbajack.FileExtractor": "0.4.4",
"Wabbajack.Networking.WabbajackClientApi": "0.4.4",
"Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.VFS": "0.4.4",
"ini-parser-netstandard": "2.5.2"
},
"runtime": {
"Wabbajack.Installer.dll": {}
}
},
"Wabbajack.IO.Async/0.4.3": {
"Wabbajack.IO.Async/0.4.4": {
"runtime": {
"Wabbajack.IO.Async.dll": {}
}
},
"Wabbajack.Networking.BethesdaNet/0.4.3": {
"Wabbajack.Networking.BethesdaNet/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3"
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Networking.BethesdaNet.dll": {}
}
},
"Wabbajack.Networking.Discord/0.4.3": {
"Wabbajack.Networking.Discord/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Networking.Http.Interfaces": "0.4.3"
"Wabbajack.Networking.Http.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Networking.Discord.dll": {}
}
},
"Wabbajack.Networking.GitHub/0.4.3": {
"Wabbajack.Networking.GitHub/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3"
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.Networking.GitHub.dll": {}
}
},
"Wabbajack.Networking.Http/0.4.3": {
"Wabbajack.Networking.Http/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Http": "9.0.1",
"Microsoft.Extensions.Logging": "9.0.1",
"Wabbajack.Configuration": "0.4.3",
"Wabbajack.Downloaders.Interfaces": "0.4.3",
"Wabbajack.Hashing.xxHash64": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3",
"Wabbajack.Paths": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3"
"Wabbajack.Configuration": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4"
},
"runtime": {
"Wabbajack.Networking.Http.dll": {}
}
},
"Wabbajack.Networking.Http.Interfaces/0.4.3": {
"Wabbajack.Networking.Http.Interfaces/0.4.4": {
"dependencies": {
"Wabbajack.Hashing.xxHash64": "0.4.3"
"Wabbajack.Hashing.xxHash64": "0.4.4"
},
"runtime": {
"Wabbajack.Networking.Http.Interfaces.dll": {}
}
},
"Wabbajack.Networking.NexusApi/0.4.3": {
"Wabbajack.Networking.NexusApi/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Networking.Http": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3",
"Wabbajack.Networking.WabbajackClientApi": "0.4.3"
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Networking.WabbajackClientApi": "0.4.4"
},
"runtime": {
"Wabbajack.Networking.NexusApi.dll": {}
}
},
"Wabbajack.Networking.WabbajackClientApi/0.4.3": {
"Wabbajack.Networking.WabbajackClientApi/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0",
"Wabbajack.Common": "0.4.3",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3",
"Wabbajack.VFS.Interfaces": "0.4.3",
"Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.VFS.Interfaces": "0.4.4",
"YamlDotNet": "16.3.0"
},
"runtime": {
"Wabbajack.Networking.WabbajackClientApi.dll": {}
}
},
"Wabbajack.Paths/0.4.3": {
"Wabbajack.Paths/0.4.4": {
"runtime": {
"Wabbajack.Paths.dll": {}
}
},
"Wabbajack.Paths.IO/0.4.3": {
"Wabbajack.Paths.IO/0.4.4": {
"dependencies": {
"Wabbajack.Paths": "0.4.3",
"Wabbajack.Paths": "0.4.4",
"shortid": "4.0.0"
},
"runtime": {
"Wabbajack.Paths.IO.dll": {}
}
},
"Wabbajack.RateLimiter/0.4.3": {
"Wabbajack.RateLimiter/0.4.4": {
"runtime": {
"Wabbajack.RateLimiter.dll": {}
}
},
"Wabbajack.Server.Lib/0.4.3": {
"Wabbajack.Server.Lib/0.4.4": {
"dependencies": {
"FluentFTP": "52.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -2264,58 +2264,58 @@
"Nettle": "3.0.0",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.4.3",
"Wabbajack.Networking.Http.Interfaces": "0.4.3",
"Wabbajack.Services.OSIntegrated": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Services.OSIntegrated": "0.4.4"
},
"runtime": {
"Wabbajack.Server.Lib.dll": {}
}
},
"Wabbajack.Services.OSIntegrated/0.4.3": {
"Wabbajack.Services.OSIntegrated/0.4.4": {
"dependencies": {
"DeviceId": "6.8.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Compiler": "0.4.3",
"Wabbajack.Downloaders.Dispatcher": "0.4.3",
"Wabbajack.Installer": "0.4.3",
"Wabbajack.Networking.BethesdaNet": "0.4.3",
"Wabbajack.Networking.Discord": "0.4.3",
"Wabbajack.VFS": "0.4.3"
"Wabbajack.Compiler": "0.4.4",
"Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Installer": "0.4.4",
"Wabbajack.Networking.BethesdaNet": "0.4.4",
"Wabbajack.Networking.Discord": "0.4.4",
"Wabbajack.VFS": "0.4.4"
},
"runtime": {
"Wabbajack.Services.OSIntegrated.dll": {}
}
},
"Wabbajack.VFS/0.4.3": {
"Wabbajack.VFS/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6",
"System.Data.SQLite.Core": "1.0.119",
"Wabbajack.Common": "0.4.3",
"Wabbajack.FileExtractor": "0.4.3",
"Wabbajack.Hashing.PHash": "0.4.3",
"Wabbajack.Hashing.xxHash64": "0.4.3",
"Wabbajack.Paths": "0.4.3",
"Wabbajack.Paths.IO": "0.4.3",
"Wabbajack.VFS.Interfaces": "0.4.3"
"Wabbajack.Common": "0.4.4",
"Wabbajack.FileExtractor": "0.4.4",
"Wabbajack.Hashing.PHash": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.VFS.Interfaces": "0.4.4"
},
"runtime": {
"Wabbajack.VFS.dll": {}
}
},
"Wabbajack.VFS.Interfaces/0.4.3": {
"Wabbajack.VFS.Interfaces/0.4.4": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.4.3",
"Wabbajack.Hashing.xxHash64": "0.4.3",
"Wabbajack.Paths": "0.4.3"
"Wabbajack.DTOs": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Paths": "0.4.4"
},
"runtime": {
"Wabbajack.VFS.Interfaces.dll": {}
@@ -2332,7 +2332,7 @@
}
},
"libraries": {
"jackify-engine/0.4.3": {
"jackify-engine/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
@@ -3021,202 +3021,202 @@
"path": "yamldotnet/16.3.0",
"hashPath": "yamldotnet.16.3.0.nupkg.sha512"
},
"Wabbajack.CLI.Builder/0.4.3": {
"Wabbajack.CLI.Builder/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Common/0.4.3": {
"Wabbajack.Common/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compiler/0.4.3": {
"Wabbajack.Compiler/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compression.BSA/0.4.3": {
"Wabbajack.Compression.BSA/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compression.Zip/0.4.3": {
"Wabbajack.Compression.Zip/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Configuration/0.4.3": {
"Wabbajack.Configuration/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Bethesda/0.4.3": {
"Wabbajack.Downloaders.Bethesda/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Dispatcher/0.4.3": {
"Wabbajack.Downloaders.Dispatcher/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.GameFile/0.4.3": {
"Wabbajack.Downloaders.GameFile/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.GoogleDrive/0.4.3": {
"Wabbajack.Downloaders.GoogleDrive/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Http/0.4.3": {
"Wabbajack.Downloaders.Http/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Interfaces/0.4.3": {
"Wabbajack.Downloaders.Interfaces/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.3": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Manual/0.4.3": {
"Wabbajack.Downloaders.Manual/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.MediaFire/0.4.3": {
"Wabbajack.Downloaders.MediaFire/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Mega/0.4.3": {
"Wabbajack.Downloaders.Mega/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.ModDB/0.4.3": {
"Wabbajack.Downloaders.ModDB/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Nexus/0.4.3": {
"Wabbajack.Downloaders.Nexus/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.VerificationCache/0.4.3": {
"Wabbajack.Downloaders.VerificationCache/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.WabbajackCDN/0.4.3": {
"Wabbajack.Downloaders.WabbajackCDN/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.DTOs/0.4.3": {
"Wabbajack.DTOs/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.FileExtractor/0.4.3": {
"Wabbajack.FileExtractor/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Hashing.PHash/0.4.3": {
"Wabbajack.Hashing.PHash/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Hashing.xxHash64/0.4.3": {
"Wabbajack.Hashing.xxHash64/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Installer/0.4.3": {
"Wabbajack.Installer/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.IO.Async/0.4.3": {
"Wabbajack.IO.Async/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.BethesdaNet/0.4.3": {
"Wabbajack.Networking.BethesdaNet/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Discord/0.4.3": {
"Wabbajack.Networking.Discord/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.GitHub/0.4.3": {
"Wabbajack.Networking.GitHub/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Http/0.4.3": {
"Wabbajack.Networking.Http/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Http.Interfaces/0.4.3": {
"Wabbajack.Networking.Http.Interfaces/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.NexusApi/0.4.3": {
"Wabbajack.Networking.NexusApi/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.WabbajackClientApi/0.4.3": {
"Wabbajack.Networking.WabbajackClientApi/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Paths/0.4.3": {
"Wabbajack.Paths/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Paths.IO/0.4.3": {
"Wabbajack.Paths.IO/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.RateLimiter/0.4.3": {
"Wabbajack.RateLimiter/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Server.Lib/0.4.3": {
"Wabbajack.Server.Lib/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Services.OSIntegrated/0.4.3": {
"Wabbajack.Services.OSIntegrated/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.VFS/0.4.3": {
"Wabbajack.VFS/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.VFS.Interfaces/0.4.3": {
"Wabbajack.VFS.Interfaces/0.4.4": {
"type": "project",
"serviceable": false,
"sha512": ""

Binary file not shown.

View File

@@ -904,20 +904,22 @@ class SettingsDialog(QDialog):
if best_proton:
resolved_install_path = str(best_proton['path'])
resolved_install_version = best_proton['name']
self.config_handler.set("proton_path", resolved_install_path)
self.config_handler.set("proton_version", resolved_install_version)
else:
resolved_install_path = "auto"
resolved_install_version = "auto"
except:
resolved_install_path = "auto"
resolved_install_version = "auto"
# No Proton found - don't write anything, let engine auto-detect
logger.warning("Auto Proton selection failed: No Proton versions found")
# Don't modify existing config values
except Exception as e:
# Exception during detection - log it and don't write anything
logger.error(f"Auto Proton selection failed with exception: {e}", exc_info=True)
# Don't modify existing config values
else:
# User selected specific Proton version
resolved_install_path = selected_install_proton_path
# Extract version from dropdown text
resolved_install_version = self.install_proton_dropdown.currentText()
self.config_handler.set("proton_path", resolved_install_path)
self.config_handler.set("proton_version", resolved_install_version)
self.config_handler.set("proton_path", resolved_install_path)
self.config_handler.set("proton_version", resolved_install_version)
# Save Game Proton selection
selected_game_proton_path = self.game_proton_dropdown.currentData()
@@ -1038,6 +1040,10 @@ class JackifyMainWindow(QMainWindow):
self._details_extra_height = 360
self._initial_show_adjusted = False
# Track open dialogs to prevent duplicates
self._settings_dialog = None
self._about_dialog = None
# Ensure GNOME/Ubuntu exposes full set of window controls (avoid hidden buttons)
self._apply_standard_window_flags()
try:
@@ -1606,23 +1612,74 @@ class JackifyMainWindow(QMainWindow):
event.accept()
def open_settings_dialog(self):
"""Open settings dialog, preventing duplicate instances"""
try:
# Check if dialog already exists and is visible
if self._settings_dialog is not None:
try:
if self._settings_dialog.isVisible():
# Dialog is already open - raise it to front
self._settings_dialog.raise_()
self._settings_dialog.activateWindow()
return
else:
# Dialog exists but is closed - clean up reference
self._settings_dialog = None
except RuntimeError:
# Dialog was deleted - clean up reference
self._settings_dialog = None
# Create new dialog
dlg = SettingsDialog(self)
self._settings_dialog = dlg
# Clean up reference when dialog is closed
def on_dialog_finished():
self._settings_dialog = None
dlg.finished.connect(on_dialog_finished)
dlg.exec()
except Exception as e:
print(f"[ERROR] Exception in open_settings_dialog: {e}")
import traceback
traceback.print_exc()
self._settings_dialog = None
def open_about_dialog(self):
"""Open about dialog, preventing duplicate instances"""
try:
from jackify.frontends.gui.dialogs.about_dialog import AboutDialog
# Check if dialog already exists and is visible
if self._about_dialog is not None:
try:
if self._about_dialog.isVisible():
# Dialog is already open - raise it to front
self._about_dialog.raise_()
self._about_dialog.activateWindow()
return
else:
# Dialog exists but is closed - clean up reference
self._about_dialog = None
except RuntimeError:
# Dialog was deleted - clean up reference
self._about_dialog = None
# Create new dialog
dlg = AboutDialog(self.system_info, self)
self._about_dialog = dlg
# Clean up reference when dialog is closed
def on_dialog_finished():
self._about_dialog = None
dlg.finished.connect(on_dialog_finished)
dlg.exec()
except Exception as e:
print(f"[ERROR] Exception in open_about_dialog: {e}")
import traceback
traceback.print_exc()
self._about_dialog = None
def _open_url(self, url: str):
"""Open URL with clean environment to avoid AppImage library conflicts."""

View File

@@ -833,6 +833,8 @@ class ModlistGalleryDialog(QDialog):
self._validation_update_timer = None # Timer for background validation updates
self._setup_ui()
# Disable filter controls during initial load to prevent race conditions
self._set_filter_controls_enabled(False)
# Lazy load - fetch modlists when dialog is shown
def _apply_initial_size(self):
@@ -1168,6 +1170,9 @@ class ModlistGalleryDialog(QDialog):
# Reconnect filter handler
self.game_combo.currentIndexChanged.connect(self._apply_filters)
# Enable filter controls now that data is loaded
self._set_filter_controls_enabled(True)
# Apply filters (will show all modlists for selected game initially)
self._apply_filters()
@@ -1389,8 +1394,23 @@ class ModlistGalleryDialog(QDialog):
self._filter_mods_list() # Refresh mod list based on NSFW state
self._apply_filters() # Apply all filters
def _set_filter_controls_enabled(self, enabled: bool):
"""Enable or disable all filter controls"""
self.search_box.setEnabled(enabled)
self.game_combo.setEnabled(enabled)
self.show_official_only.setEnabled(enabled)
self.show_nsfw.setEnabled(enabled)
self.hide_unavailable.setEnabled(enabled)
self.tags_list.setEnabled(enabled)
self.mod_search.setEnabled(enabled)
self.mods_list.setEnabled(enabled)
def _apply_filters(self):
"""Apply current filters to modlist display"""
# CRITICAL: Guard against race condition - don't filter if modlists aren't loaded yet
if not self.all_modlists:
return
filtered = self.all_modlists
# Search filter
@@ -1480,15 +1500,25 @@ class ModlistGalleryDialog(QDialog):
def _update_grid(self):
"""Update grid by removing all cards and re-adding only visible ones"""
# CRITICAL: Guard against race condition - don't update if cards aren't ready yet
if not self.all_cards:
return
# Disable updates during grid update
self.grid_widget.setUpdatesEnabled(False)
try:
# Remove all cards from layout
# CRITICAL FIX: Properly remove widgets to prevent overlapping and orphaned windows
# We need to explicitly remove widgets from the layout before taking items
# to ensure they're fully cleaned up, but we don't setParent(None) because
# widgets are immediately re-added to the grid (Qt will reparent them).
while self.grid_layout.count():
item = self.grid_layout.takeAt(0)
if item.widget():
item.widget().setParent(None)
widget = item.widget() if item else None
if widget:
# Explicitly remove widget from layout to prevent overlapping
self.grid_layout.removeWidget(widget)
del item
# Calculate number of columns based on available width
@@ -1528,6 +1558,16 @@ class ModlistGalleryDialog(QDialog):
card = self.all_cards.get(modlist.machineURL)
if card:
# Ensure widget is not already in the layout (prevent overlapping)
# If it is, remove it first (shouldn't happen after takeAt, but safety check)
if card.parent() == self.grid_widget:
# Widget is already a child of grid_widget, check if it's in layout
for i in range(self.grid_layout.count()):
item = self.grid_layout.itemAt(i)
if item and item.widget() == card:
# Already in layout, remove it first
self.grid_layout.removeWidget(card)
break
self.grid_layout.addWidget(card, row, col)
# Set column stretch - don't stretch card columns, but add a spacer column

View File

@@ -20,6 +20,13 @@ from PySide6.QtGui import QFont
from jackify.shared.progress_models import FileProgress, OperationType
from ..shared_theme import JACKIFY_COLOR_BLUE
def _debug_log(message):
"""Log message only if debug mode is enabled"""
from jackify.backend.handlers.config_handler import ConfigHandler
config_handler = ConfigHandler()
if config_handler.get('debug_mode', False):
print(message)
class SummaryProgressWidget(QWidget):
"""Widget showing summary progress for phases like Installing."""
@@ -484,7 +491,28 @@ class FileProgressList(QWidget):
return
# Widget doesn't exist - create it (only clear when creating new widget)
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] About to clear list_widget for summary widget - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear (summary) - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear()
# Create new summary widget
@@ -510,7 +538,22 @@ class FileProgressList(QWidget):
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item and item.data(Qt.UserRole) == "__summary__":
# CRITICAL FIX: Call removeItemWidget() before takeItem() to prevent orphaned widgets
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing summary widget - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Summary widget became top-level window after removeItemWidget()!")
import traceback
traceback.print_stack()
self.list_widget.takeItem(i)
if widget:
_debug_log(f"[WIDGET_FIX] After takeItem (summary) - widget.parent()={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Summary widget is still a top-level window after takeItem()!")
import traceback
traceback.print_stack()
break
self._summary_widget = None
else:
@@ -522,7 +565,22 @@ class FileProgressList(QWidget):
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item and item.data(Qt.UserRole) == "__transition__":
# CRITICAL FIX: Call removeItemWidget() before takeItem() to prevent orphaned widgets
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing transition label - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Transition label became top-level window after removeItemWidget()!")
import traceback
traceback.print_stack()
self.list_widget.takeItem(i)
if widget:
_debug_log(f"[WIDGET_FIX] After takeItem (transition) - widget.parent()={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Transition label is still a top-level window after takeItem()!")
import traceback
traceback.print_stack()
break
self._transition_label = None
@@ -533,7 +591,28 @@ class FileProgressList(QWidget):
self._show_transition_message(current_phase)
else:
# Show empty state but keep header stable
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] About to clear list_widget (empty state) - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear (empty) - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear (empty) - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear()
# Update last phase tracker
@@ -579,7 +658,24 @@ class FileProgressList(QWidget):
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item and item.data(Qt.UserRole) == item_key:
# CRITICAL FIX: Call removeItemWidget() before takeItem() to prevent orphaned widgets
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget for item_key={item_key} - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
# Check if widget became orphaned after removal
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget()! widget={widget}")
import traceback
traceback.print_stack()
self.list_widget.takeItem(i)
# Final check after takeItem
if widget:
_debug_log(f"[WIDGET_FIX] After takeItem - widget.parent()={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget is still a top-level window after takeItem()! This is the bug!")
import traceback
traceback.print_stack()
break
del self._file_items[item_key]
@@ -638,7 +734,28 @@ class FileProgressList(QWidget):
def _show_transition_message(self, new_phase: str):
"""Show a brief 'Preparing...' message during phase transitions."""
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] About to clear list_widget (transition) - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear (transition) - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear (transition) - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear()
# Header removed - tab label provides context
@@ -663,9 +780,42 @@ class FileProgressList(QWidget):
def clear(self):
"""Clear all file items."""
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] clear() called - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear() - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear() - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear()
if self._summary_widget:
_debug_log(f"[WIDGET_FIX] Clearing summary_widget - widget={self._summary_widget}, parent={self._summary_widget.parent()}, isWindow()={self._summary_widget.isWindow()}")
if self._summary_widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Summary widget is a top-level window in clear()!")
import traceback
traceback.print_stack()
self._summary_widget = None
if self._transition_label:
_debug_log(f"[WIDGET_FIX] Clearing transition_label - widget={self._transition_label}, parent={self._transition_label.parent()}, isWindow()={self._transition_label.isWindow()}")
if self._transition_label.isWindow():
print(f"[WIDGET_FIX] ERROR: Transition label is a top-level window in clear()!")
import traceback
traceback.print_stack()
self._transition_label = None
self._last_phase = None
# Header removed - tab label provides context