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
+13
View File
@@ -1,5 +1,18 @@
# Jackify Changelog
## v0.2.0.6 - Premium Detection and Engine Update
**Release Date:** 2025-12-28
### Engine Updates
- **jackify-engine 0.4.4**: Latest engine version with improvements
### Critical Bug Fixes
- **Premium Detection**: Fixed false Premium errors caused by overly-broad detection pattern triggering on jackify-engine 0.4.3's userinfo JSON output
- **Custom Data Directory**: Fixed AppImage always creating ~/Jackify on startup, even when user configured a custom jackify_data_dir
- **Proton Auto-Selection**: Fixed auto-selection writing invalid "auto" string to config on detection failure
---
## v0.2.0.5 - Emergency OAuth Fix
**Release Date:** 2025-12-24
+1 -1
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"
@@ -143,6 +143,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
+4 -1
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']:
@@ -71,7 +74,7 @@ def get_clean_subprocess_env(extra_env=None):
# 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:
@@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+225 -225
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.
+66 -9
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."""
@@ -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
@@ -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